From eb3bf3241ed75bb09afb4752f5dbbf4c0ae0de45 Mon Sep 17 00:00:00 2001 From: Acammm Date: Fri, 19 Nov 2021 12:05:05 +0100 Subject: [PATCH 001/204] Add UXDProtocol --- models/registry/api.ts | 9 +++++++++ public/realms/UXDProtocol/img/logo.png | Bin 0 -> 22730 bytes 2 files changed, 9 insertions(+) create mode 100644 public/realms/UXDProtocol/img/logo.png diff --git a/models/registry/api.ts b/models/registry/api.ts index 075e801022..e18fc51a34 100644 --- a/models/registry/api.ts +++ b/models/registry/api.ts @@ -129,6 +129,15 @@ const MAINNET_REALMS: RealmInfo[] = [ website: 'https://www.solanasail.com/', twitter: '@SolanaSail', }, + { + symbol: 'UXP', + displayName: 'UXD Protocol DAO', + programId: new PublicKey('GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw'), + realmId: new PublicKey('DkSvNgykZPPFczhJVh8HDkhz25ByrDoPcB32q75AYu9k'), + ogImage: '/realms/UXDProtocol/img/logo.png', + website: 'https://www.solanasail.com/', + twitter: '@SolanaSail', + }, ] // Hardcoded list of devnet realms diff --git a/public/realms/UXDProtocol/img/logo.png b/public/realms/UXDProtocol/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8e282bb4a7f50955762d7926bfb314315b1a9091 GIT binary patch literal 22730 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 literal 0 HcmV?d00001 From 87618d83155a00131cefb3ec2572e18d714eccb9 Mon Sep 17 00:00:00 2001 From: acamill <40299747+acamill@users.noreply.github.com> Date: Fri, 19 Nov 2021 03:15:55 -0800 Subject: [PATCH 002/204] Add our rpc node --- stores/useWalletStore.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stores/useWalletStore.tsx b/stores/useWalletStore.tsx index 5080f35782..9191ab711c 100644 --- a/stores/useWalletStore.tsx +++ b/stores/useWalletStore.tsx @@ -146,7 +146,7 @@ async function resolveProposalDescription(description: string) { export const ENDPOINTS: EndpointInfo[] = [ { name: 'mainnet', - url: process.env.MAINNET_RPC || 'https://mango.rpcpool.com', + url: process.env.MAINNET_RPC || 'https://billowing-proud-glitter.solana-mainnet.quiknode.pro/7f49d7f436c2d3af0738270d90dee86962f13a82/', }, { name: 'devnet', From 25e280eac6427e48d3b987f0c9b901bae688cedf Mon Sep 17 00:00:00 2001 From: Acammm Date: Fri, 19 Nov 2021 13:01:36 +0100 Subject: [PATCH 003/204] Update the Token name to UXD Council Token for now --- models/registry/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/registry/api.ts b/models/registry/api.ts index e18fc51a34..8fdc831685 100644 --- a/models/registry/api.ts +++ b/models/registry/api.ts @@ -130,7 +130,7 @@ const MAINNET_REALMS: RealmInfo[] = [ twitter: '@SolanaSail', }, { - symbol: 'UXP', + symbol: 'UXD Council Token', displayName: 'UXD Protocol DAO', programId: new PublicKey('GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw'), realmId: new PublicKey('DkSvNgykZPPFczhJVh8HDkhz25ByrDoPcB32q75AYu9k'), From a6ebb49dc8575fcbd0b6a8f38f0d5ef9a4ef29f0 Mon Sep 17 00:00:00 2001 From: acamill <40299747+acamill@users.noreply.github.com> Date: Fri, 19 Nov 2021 08:50:08 -0800 Subject: [PATCH 004/204] Update site and twitter --- models/registry/api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/registry/api.ts b/models/registry/api.ts index 8fdc831685..bad8d29945 100644 --- a/models/registry/api.ts +++ b/models/registry/api.ts @@ -135,8 +135,8 @@ const MAINNET_REALMS: RealmInfo[] = [ programId: new PublicKey('GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw'), realmId: new PublicKey('DkSvNgykZPPFczhJVh8HDkhz25ByrDoPcB32q75AYu9k'), ogImage: '/realms/UXDProtocol/img/logo.png', - website: 'https://www.solanasail.com/', - twitter: '@SolanaSail', + website: 'https://www.uxd.fi.com/', + twitter: '@UXDProtocol', }, ] From ff410b961be3593a59f44731eeac93a2c71d47c3 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 12 Dec 2021 10:32:05 +0900 Subject: [PATCH 005/204] update gitignore --- .gitignore | 3 +++ stores/useWalletStore.tsx | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1437c53f70..e9dde829b5 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ yarn-error.log* # vercel .vercel + + +.npmrc \ No newline at end of file diff --git a/stores/useWalletStore.tsx b/stores/useWalletStore.tsx index 25f2217821..cf7af0fe97 100644 --- a/stores/useWalletStore.tsx +++ b/stores/useWalletStore.tsx @@ -150,7 +150,9 @@ async function resolveProposalDescription(description: string) { export const ENDPOINTS: EndpointInfo[] = [ { name: 'mainnet', - url: process.env.MAINNET_RPC || 'https://billowing-proud-glitter.solana-mainnet.quiknode.pro/7f49d7f436c2d3af0738270d90dee86962f13a82/', + url: + process.env.MAINNET_RPC || + 'https://billowing-proud-glitter.solana-mainnet.quiknode.pro/7f49d7f436c2d3af0738270d90dee86962f13a82/', }, { name: 'devnet', From 0df8439157b5ec7aeb2425d8c82101821d71ceb4 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Tue, 30 Nov 2021 16:20:12 +0900 Subject: [PATCH 006/204] add uxd dao to devnet --- stores/useWalletStore.tsx | 4 +- test/pages/__snapshots__/index.test.tsx.snap | 137 ------------------- 2 files changed, 2 insertions(+), 139 deletions(-) delete mode 100644 test/pages/__snapshots__/index.test.tsx.snap diff --git a/stores/useWalletStore.tsx b/stores/useWalletStore.tsx index cf7af0fe97..79fb16ffde 100644 --- a/stores/useWalletStore.tsx +++ b/stores/useWalletStore.tsx @@ -156,7 +156,7 @@ export const ENDPOINTS: EndpointInfo[] = [ }, { name: 'devnet', - url: process.env.DEVNET_RPC || 'https://mango.devnet.rpcpool.com', + url: process.env.DEVNET_RPC || 'https://api.devnet.solana.com', }, { name: 'localnet', @@ -166,6 +166,7 @@ export const ENDPOINTS: EndpointInfo[] = [ export function getConnectionContext(cluster: string): ConnectionContext { const ENDPOINT = ENDPOINTS.find((e) => e.name === cluster) || ENDPOINTS[0] + console.log({ ENDPOINT }) return { cluster: ENDPOINT!.name as EndpointTypes, current: new Connection(ENDPOINT!.url, 'recent'), @@ -220,7 +221,6 @@ const useWalletStore = create((set, get) => ({ const wallet = get().current const walletOwner = wallet?.publicKey const set = get().set - if (connected && walletOwner) { const ownedTokenAccounts = await getOwnedTokenAccounts( connection, diff --git a/test/pages/__snapshots__/index.test.tsx.snap b/test/pages/__snapshots__/index.test.tsx.snap deleted file mode 100644 index a279bd0110..0000000000 --- a/test/pages/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,137 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Home page matches snapshot 1`] = ` - - - -`; From e9bb62721833c44f885c2399beae884db34f1287 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 5 Dec 2021 02:11:48 +0900 Subject: [PATCH 007/204] bump anchor version and add uxd idl --- package.json | 2 +- tools/sdk/uxdProtocol/uxdIdl.ts | 921 ++++++++++++++++++++++++++++++++ yarn.lock | 24 +- 3 files changed, 934 insertions(+), 13 deletions(-) create mode 100644 tools/sdk/uxdProtocol/uxdIdl.ts diff --git a/package.json b/package.json index 2e48612649..dd388f8647 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@emotion/styled": "^11.3.0", "@headlessui/react": "^1.0.0", "@heroicons/react": "^1.0.1", - "@project-serum/anchor": "^0.10.0", + "@project-serum/anchor": "^0.18.2", "@project-serum/borsh": "^0.2.2", "@project-serum/common": "^0.0.1-beta.3", "@project-serum/sol-wallet-adapter": "^0.2.0", diff --git a/tools/sdk/uxdProtocol/uxdIdl.ts b/tools/sdk/uxdProtocol/uxdIdl.ts new file mode 100644 index 0000000000..5388476777 --- /dev/null +++ b/tools/sdk/uxdProtocol/uxdIdl.ts @@ -0,0 +1,921 @@ +import { Idl } from '@project-serum/anchor' + +const uxdIdl: Idl = { + version: '0.0.0', + name: 'uxd', + instructions: [ + { + name: 'initializeController', + accounts: [ + { + name: 'authority', + isMut: true, + isSigner: true, + }, + { + name: 'controller', + isMut: true, + isSigner: false, + }, + { + name: 'redeemableMint', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'rent', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'redeemableMintBump', + type: 'u8', + }, + { + name: 'redeemableMintDecimals', + type: 'u8', + }, + ], + }, + { + name: 'setRedeemableGlobalSupplyCap', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'controller', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'redeemableGlobalSupplyCap', + type: 'u128', + }, + ], + }, + { + name: 'setMangoDepositoriesRedeemableSoftCap', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'controller', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'redeemableSoftCap', + type: 'u64', + }, + ], + }, + { + name: 'registerMangoDepository', + accounts: [ + { + name: 'authority', + isMut: true, + isSigner: true, + }, + { + name: 'controller', + isMut: true, + isSigner: false, + }, + { + name: 'depository', + isMut: true, + isSigner: false, + }, + { + name: 'collateralMint', + isMut: false, + isSigner: false, + }, + { + name: 'insuranceMint', + isMut: false, + isSigner: false, + }, + { + name: 'depositoryCollateralPassthroughAccount', + isMut: true, + isSigner: false, + }, + { + name: 'depositoryInsurancePassthroughAccount', + isMut: true, + isSigner: false, + }, + { + name: 'depositoryMangoAccount', + isMut: true, + isSigner: false, + }, + { + name: 'mangoGroup', + isMut: false, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'mangoProgram', + isMut: false, + isSigner: false, + }, + { + name: 'rent', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'collateralPassthroughBump', + type: 'u8', + }, + { + name: 'insurancePassthroughBump', + type: 'u8', + }, + { + name: 'mangoAccountBump', + type: 'u8', + }, + ], + }, + { + name: 'depositInsuranceToMangoDepository', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'controller', + isMut: true, + isSigner: false, + }, + { + name: 'depository', + isMut: true, + isSigner: false, + }, + { + name: 'collateralMint', + isMut: false, + isSigner: false, + }, + { + name: 'insuranceMint', + isMut: false, + isSigner: false, + }, + { + name: 'authorityInsurance', + isMut: true, + isSigner: false, + }, + { + name: 'depositoryInsurancePassthroughAccount', + isMut: true, + isSigner: false, + }, + { + name: 'depositoryMangoAccount', + isMut: true, + isSigner: false, + }, + { + name: 'mangoGroup', + isMut: false, + isSigner: false, + }, + { + name: 'mangoCache', + isMut: false, + isSigner: false, + }, + { + name: 'mangoRootBank', + isMut: false, + isSigner: false, + }, + { + name: 'mangoNodeBank', + isMut: true, + isSigner: false, + }, + { + name: 'mangoVault', + isMut: true, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'mangoProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'insuranceAmount', + type: 'u64', + }, + ], + }, + { + name: 'withdrawInsuranceFromMangoDepository', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'controller', + isMut: true, + isSigner: false, + }, + { + name: 'depository', + isMut: true, + isSigner: false, + }, + { + name: 'collateralMint', + isMut: false, + isSigner: false, + }, + { + name: 'insuranceMint', + isMut: false, + isSigner: false, + }, + { + name: 'authorityInsurance', + isMut: true, + isSigner: false, + }, + { + name: 'depositoryInsurancePassthroughAccount', + isMut: true, + isSigner: false, + }, + { + name: 'depositoryMangoAccount', + isMut: true, + isSigner: false, + }, + { + name: 'mangoGroup', + isMut: false, + isSigner: false, + }, + { + name: 'mangoCache', + isMut: false, + isSigner: false, + }, + { + name: 'mangoSigner', + isMut: false, + isSigner: false, + }, + { + name: 'mangoRootBank', + isMut: false, + isSigner: false, + }, + { + name: 'mangoNodeBank', + isMut: true, + isSigner: false, + }, + { + name: 'mangoVault', + isMut: true, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'mangoProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'insuranceAmount', + type: 'u64', + }, + ], + }, + { + name: 'mintWithMangoDepository', + accounts: [ + { + name: 'user', + isMut: false, + isSigner: true, + }, + { + name: 'controller', + isMut: true, + isSigner: false, + }, + { + name: 'depository', + isMut: true, + isSigner: false, + }, + { + name: 'collateralMint', + isMut: false, + isSigner: false, + }, + { + name: 'redeemableMint', + isMut: true, + isSigner: false, + }, + { + name: 'userCollateral', + isMut: true, + isSigner: false, + }, + { + name: 'userRedeemable', + isMut: true, + isSigner: false, + }, + { + name: 'depositoryCollateralPassthroughAccount', + isMut: true, + isSigner: false, + }, + { + name: 'depositoryMangoAccount', + isMut: true, + isSigner: false, + }, + { + name: 'mangoGroup', + isMut: false, + isSigner: false, + }, + { + name: 'mangoCache', + isMut: false, + isSigner: false, + }, + { + name: 'mangoRootBank', + isMut: false, + isSigner: false, + }, + { + name: 'mangoNodeBank', + isMut: true, + isSigner: false, + }, + { + name: 'mangoVault', + isMut: true, + isSigner: false, + }, + { + name: 'mangoPerpMarket', + isMut: true, + isSigner: false, + }, + { + name: 'mangoBids', + isMut: true, + isSigner: false, + }, + { + name: 'mangoAsks', + isMut: true, + isSigner: false, + }, + { + name: 'mangoEventQueue', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'mangoProgram', + isMut: false, + isSigner: false, + }, + { + name: 'rent', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'collateralAmount', + type: 'u64', + }, + { + name: 'slippage', + type: 'u32', + }, + ], + }, + { + name: 'redeemFromMangoDepository', + accounts: [ + { + name: 'user', + isMut: false, + isSigner: true, + }, + { + name: 'controller', + isMut: true, + isSigner: false, + }, + { + name: 'depository', + isMut: true, + isSigner: false, + }, + { + name: 'collateralMint', + isMut: false, + isSigner: false, + }, + { + name: 'userCollateral', + isMut: true, + isSigner: false, + }, + { + name: 'userRedeemable', + isMut: true, + isSigner: false, + }, + { + name: 'redeemableMint', + isMut: true, + isSigner: false, + }, + { + name: 'depositoryCollateralPassthroughAccount', + isMut: true, + isSigner: false, + }, + { + name: 'depositoryMangoAccount', + isMut: true, + isSigner: false, + }, + { + name: 'mangoGroup', + isMut: false, + isSigner: false, + }, + { + name: 'mangoCache', + isMut: false, + isSigner: false, + }, + { + name: 'mangoSigner', + isMut: false, + isSigner: false, + }, + { + name: 'mangoRootBank', + isMut: false, + isSigner: false, + }, + { + name: 'mangoNodeBank', + isMut: true, + isSigner: false, + }, + { + name: 'mangoVault', + isMut: true, + isSigner: false, + }, + { + name: 'mangoPerpMarket', + isMut: true, + isSigner: false, + }, + { + name: 'mangoBids', + isMut: true, + isSigner: false, + }, + { + name: 'mangoAsks', + isMut: true, + isSigner: false, + }, + { + name: 'mangoEventQueue', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'mangoProgram', + isMut: false, + isSigner: false, + }, + { + name: 'rent', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'redeemableAmount', + type: 'u64', + }, + { + name: 'slippage', + type: 'u32', + }, + ], + }, + ], + accounts: [ + { + name: 'Controller', + type: { + kind: 'struct', + fields: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'redeemableMintBump', + type: 'u8', + }, + { + name: 'version', + type: 'u8', + }, + { + name: 'authority', + type: 'publicKey', + }, + { + name: 'redeemableMint', + type: 'publicKey', + }, + { + name: 'redeemableMintDecimals', + type: 'u8', + }, + { + name: 'registeredMangoDepositories', + type: { + array: ['publicKey', 8], + }, + }, + { + name: 'registeredMangoDepositoriesCount', + type: 'u8', + }, + { + name: 'redeemableGlobalSupplyCap', + type: 'u128', + }, + { + name: 'mangoDepositoriesRedeemableSoftCap', + type: 'u64', + }, + { + name: 'redeemableCirculatingSupply', + type: 'u128', + }, + ], + }, + }, + { + name: 'MangoDepository', + type: { + kind: 'struct', + fields: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'collateralPassthroughBump', + type: 'u8', + }, + { + name: 'insurancePassthroughBump', + type: 'u8', + }, + { + name: 'mangoAccountBump', + type: 'u8', + }, + { + name: 'version', + type: 'u8', + }, + { + name: 'collateralMint', + type: 'publicKey', + }, + { + name: 'collateralPassthrough', + type: 'publicKey', + }, + { + name: 'insuranceMint', + type: 'publicKey', + }, + { + name: 'insurancePassthrough', + type: 'publicKey', + }, + { + name: 'mangoAccount', + type: 'publicKey', + }, + { + name: 'controller', + type: 'publicKey', + }, + { + name: 'insuranceAmountDeposited', + type: 'u128', + }, + { + name: 'collateralAmountDeposited', + type: 'u128', + }, + { + name: 'redeemableAmountUnderManagement', + type: 'u128', + }, + ], + }, + }, + ], + types: [ + { + name: 'AccountingEvent', + type: { + kind: 'enum', + variants: [ + { + name: 'Deposit', + }, + { + name: 'Withdraw', + }, + ], + }, + }, + ], + errors: [ + { + code: 300, + name: 'InvalidRedeemableMintDecimals', + msg: 'The redeemable mint decimals must be between 0 and 9 (inclusive).', + }, + { + code: 301, + name: 'InvalidRedeemableGlobalSupplyCap', + msg: + 'The redeemable global supply cap must be below MAX_REDEEMABLE_GLOBAL_SUPPLY_CAP.', + }, + { + code: 302, + name: 'RootBankIndexNotFound', + msg: + 'The associated mango root bank index cannot be found for the deposited coin..', + }, + { + code: 303, + name: 'InvalidSlippage', + msg: + 'The slippage value is invalid. Must be in the [0...1000] range points.', + }, + { + code: 304, + name: 'InvalidCollateralMint', + msg: + "The provided collateral mint does not match the depository's collateral mint.", + }, + { + code: 305, + name: 'InvalidInsuranceMint', + msg: + "The provided insurance mint does not match the depository's insurance mint.", + }, + { + code: 306, + name: 'InvalidCollateralAmount', + msg: 'Collateral amount must be > 0 in order to mint.', + }, + { + code: 307, + name: 'InsuficientCollateralAmount', + msg: + 'The balance of the collateral ATA is not enough to fulfill the mint operation.', + }, + { + code: 308, + name: 'InvalidRedeemableAmount', + msg: 'The redeemable amount for redeem must be superior to 0.', + }, + { + code: 309, + name: 'InsuficientRedeemableAmount', + msg: + 'The balance of the redeemable ATA is not enough to fulfill the redeem operation.', + }, + { + code: 310, + name: 'InvalidAuthority', + msg: + 'Only the Program initializer authority can access this instructions.', + }, + { + code: 311, + name: 'InvalidRedeemableMint', + msg: "The Redeemable Mint provided does not match the Controller's one.", + }, + { + code: 312, + name: 'InvalidUserRedeemableATAMint', + msg: + "The user's Redeemable ATA's mint does not match the Controller's one.", + }, + { + code: 313, + name: 'InvalidUserCollateralATAMint', + msg: + "The user's Collateral ATA's mint does not match the Depository's one.", + }, + { + code: 314, + name: 'InvalidAuthorityInsuranceATAMint', + msg: + "The authority's Insurance ATA's mint does not match the Depository's one.", + }, + { + code: 315, + name: 'InvalidCollateralPassthroughATAMint', + msg: + "The Depository Collateral Passthrough ATA's mint does not match the Depository's one.", + }, + { + code: 316, + name: 'InvalidInsurancePassthroughATAMint', + msg: + "The Depository Insurance Passthrough ATA's mint does not match the Depository's one.", + }, + { + code: 317, + name: 'PerpOrderPartiallyFilled', + msg: + 'The perp position could not be fully filled with the provided slippage.', + }, + { + code: 318, + name: 'PositionAmountCalculation', + msg: + 'Error while getting the redeemable value of the deposited coin amount.', + }, + { + code: 319, + name: 'RedeemableGlobalSupplyCapReached', + msg: 'Minting amount would go past the Redeemable Global Supply Cap.', + }, + { + code: 320, + name: 'MangoDepositoriesSoftCapOverflow', + msg: 'Operation not allowed due to being over the Redeemable soft Cap.', + }, + { + code: 330, + name: 'MaxNumberOfMangoDepositoriesRegisteredReached', + msg: + 'Cannot register more mango depositories, the limit has been reached.', + }, + { + code: 331, + name: 'InvalidController', + msg: "The Depository's controller doesn't match the provided Controller.", + }, + { + code: 332, + name: 'InvalidDepository', + msg: 'The Depository provided is not registered with the Controller.', + }, + { + code: 333, + name: 'InvalidCollateralPassthroughAccount', + msg: "The Collateral Passthrough Account isn't the Deposiroty one.", + }, + { + code: 334, + name: 'InvalidInsurancePassthroughAccount', + msg: "The Insurance Passthrough Account isn't the Deposiroty one.", + }, + { + code: 335, + name: 'InvalidMangoAccount', + msg: "The Mango Account isn't the Deposiroty one.", + }, + { + code: 336, + name: 'InvalidInsuranceAmount', + msg: + 'The amount to withdraw from the Insurance Fund must be superior to zero..', + }, + { + code: 337, + name: 'InsuficientAuthorityInsuranceAmount', + msg: "The Insurance ATA from authority doesn't have enough balance.", + }, + ], +} + +export default uxdIdl diff --git a/yarn.lock b/yarn.lock index 948380f572..fd6f93a996 100644 --- a/yarn.lock +++ b/yarn.lock @@ -841,10 +841,10 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.3.tgz#8b68da1ebd7fc603999cf6ebee34a4899a14b88e" integrity sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ== -"@project-serum/anchor@^0.10.0": - version "0.10.0" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.10.0.tgz#e08396b7bb3cc94b279d45ac38595118dffdd617" - integrity sha512-FWw3bQy1otINPq40uOTX0h8eE3drnespaq9Xkwpy3UXj6i46xuLjsWC+x9hp76EN4pC174POeNPvB0aivatswg== +"@project-serum/anchor@^0.11.1": + version "0.11.1" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.11.1.tgz#155bff2c70652eafdcfd5559c81a83bb19cec9ff" + integrity sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA== dependencies: "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" @@ -861,10 +861,10 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/anchor@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.11.1.tgz#155bff2c70652eafdcfd5559c81a83bb19cec9ff" - integrity sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA== +"@project-serum/anchor@^0.16.2": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.16.2.tgz#b8b4ec4c749d59a224108f8d82ab68217ef752ae" + integrity sha512-wOJwObd4wOZ5tRRMCKYjeMNsEmf7vuC71KQRnw6wthhErL8c/818n4gYIZCf/1ZPl/8WPruIlmtQHDSEyy2+0Q== dependencies: "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" @@ -881,10 +881,10 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/anchor@^0.16.2": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.16.2.tgz#b8b4ec4c749d59a224108f8d82ab68217ef752ae" - integrity sha512-wOJwObd4wOZ5tRRMCKYjeMNsEmf7vuC71KQRnw6wthhErL8c/818n4gYIZCf/1ZPl/8WPruIlmtQHDSEyy2+0Q== +"@project-serum/anchor@^0.18.2": + version "0.18.2" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.18.2.tgz#0f13b5c2046446b7c24cf28763eec90febb28485" + integrity sha512-uyjiN/3Ipp+4hrZRm/hG18HzGLZyvP790LXrCsGO3IWxSl28YRhiGEpKnZycfMW94R7nxdUoE3wY67V+ZHSQBQ== dependencies: "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" From 99e37468125f3697e5cb96fa6e08c6c3390e06d2 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 5 Dec 2021 02:16:01 +0900 Subject: [PATCH 008/204] add uxd initalize controller itx --- components/instructions/programs/names.ts | 2 + .../instructions/programs/uxdProtocol.tsx | 42 +++++ components/instructions/tools.tsx | 2 + hooks/useGovernanceAssets.ts | 11 +- .../instructions/InitializeController.tsx | 157 ++++++++++++++++++ pages/dao/[symbol]/proposal/new.tsx | 3 + .../createInitializeControllerInstruction.ts | 57 +++++++ utils/uiTypes/proposalCreationTypes.ts | 17 ++ 8 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 components/instructions/programs/uxdProtocol.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx create mode 100644 tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts diff --git a/components/instructions/programs/names.ts b/components/instructions/programs/names.ts index c490eacadb..d426bb921c 100644 --- a/components/instructions/programs/names.ts +++ b/components/instructions/programs/names.ts @@ -21,6 +21,8 @@ export const PROGRAM_NAMES = { '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8': 'Raydium AMM Program', EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q: 'Raydium Staking Program', + FXY9qRsCxg6ioEFLu9ECQ5v2YF2qjeE1Tcnin3DUBW76: 'Controller UXD', + SysvarRent111111111111111111111111111111111: 'Sysvar: Rent', SysvarC1ock11111111111111111111111111111111: 'Sysvar: Clock', diff --git a/components/instructions/programs/uxdProtocol.tsx b/components/instructions/programs/uxdProtocol.tsx new file mode 100644 index 0000000000..4c3447ec95 --- /dev/null +++ b/components/instructions/programs/uxdProtocol.tsx @@ -0,0 +1,42 @@ +import { Connection } from '@solana/web3.js' +import { struct, u8 } from 'buffer-layout' +import { AccountMetaData } from '../../../models/accounts' + +const UXD_INIT_CONTROLLER_INSTRUCTION = { + FXY9qRsCxg6ioEFLu9ECQ5v2YF2qjeE1Tcnin3DUBW76: { + 1: { + name: 'UXD - Initialize Controller', + accounts: [ + 'authority', + 'controller', + 'redeemableMint', + 'systemProgram', + 'tokenProgram', + 'rent', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([ + u8('bump'), + u8('redeemableBump'), + u8('redeemableMintDecimals'), + ]) + + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, + }, +} + +export const UXD_INSTRUCTIONS = { + ...UXD_INIT_CONTROLLER_INSTRUCTION, +} diff --git a/components/instructions/tools.tsx b/components/instructions/tools.tsx index 2e09f17476..ca7bb142d9 100644 --- a/components/instructions/tools.tsx +++ b/components/instructions/tools.tsx @@ -7,6 +7,7 @@ import { MANGO_INSTRUCTIONS } from './programs/mango' import { getProgramName, isGovernanceProgram } from './programs/names' import { RAYDIUM_INSTRUCTIONS } from './programs/raydium' import { SPL_TOKEN_INSTRUCTIONS } from './programs/splToken' +import { UXD_INSTRUCTIONS } from './programs/uxdProtocol' // Well known account names displayed on the instruction card export const ACCOUNT_NAMES = { @@ -96,6 +97,7 @@ export const INSTRUCTION_DESCRIPTORS = { ...BPF_UPGRADEABLE_LOADER_INSTRUCTIONS, ...MANGO_INSTRUCTIONS, ...RAYDIUM_INSTRUCTIONS, + ...UXD_INSTRUCTIONS, } export async function getInstructionDescriptor( diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 1b4821905c..2a4d30e3c7 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -12,7 +12,7 @@ import useRealm from './useRealm' export default function useGovernanceAssets() { const { governances, tokenMints, realmTokenAccounts } = useRealm() const connection = useWalletStore((s) => s.connection.current) - const { ownVoterWeight, realm } = useRealm() + const { ownVoterWeight, realm, symbol } = useRealm() const governancesArray = Object.keys(governances).map( (key) => governances[key] @@ -44,6 +44,10 @@ export default function useGovernanceAssets() { GovernanceAccountType.MintGovernance ) + const canUseUxdInstructions = + symbol === 'UXD Protocol' && + canUseGovernanceForInstruction(GovernanceAccountType.ProgramGovernance) + const canUseAnyInstruction = realm && governancesArray.some((g) => @@ -51,6 +55,11 @@ export default function useGovernanceAssets() { ) const availableInstructions = [ + { + id: Instructions.InitializeController, + name: 'Initialize Controller', + isVisible: canUseUxdInstructions, + }, { id: Instructions.Transfer, name: 'Transfer Tokens', diff --git a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx new file mode 100644 index 0000000000..f5541933c6 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx @@ -0,0 +1,157 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + InitializeControllerForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import { Governance, GovernanceAccountType } from '@models/accounts' +import { ParsedAccount } from '@models/core/accounts' +import useWalletStore from 'stores/useWalletStore' +import { serializeInstructionToBase64 } from '@models/serialisation' +import Input from '@components/inputs/Input' +import { debounce } from '@utils/debounce' +import GovernedAccountSelect from '../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import createInitializeControllerInstruction from '@tools/sdk/uxdProtocol/createInitializeControllerInstruction' + +const InitializeController = ({ + index, + governance, +}: { + index: number + governance: ParsedAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { getGovernancesByAccountType } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountType( + GovernanceAccountType.ProgramGovernance + ).map((x) => { + return { + governance: x, + } + }) + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({ + governedAccount: undefined, + programId: programId?.toString(), + mintSymbol: '', + mintDecimals: 0, + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + async function getInstruction(): Promise { + const isValid = await validateInstruction() + let serializedInstruction = '' + if ( + isValid && + programId && + form.governedAccount?.governance?.info && + wallet?.publicKey + ) { + const createIx = createInitializeControllerInstruction( + form.governedAccount?.governance.info.governedAccount, + form.mintDecimals || 9, + form.governedAccount?.governance.pubkey, + connection.current + ) + serializedInstruction = serializeInstructionToBase64(createIx) + } + const obj: UiInstruction = { + serializedInstruction, + isValid, + governedAccount: form.governedAccount?.governance, + } + return obj + } + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + if (form.mintSymbol) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.mintSymbol]) + useEffect(() => { + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) + const schema = yup.object().shape({ + mintSymbol: yup.string().required('Mint Symbol is required'), + mintDecimals: yup.number().required('Mint Decimals is required'), + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + handleSetForm({ + value: evt.target.value, + propertyName: 'mintSymbol', + }) + } + error={formErrors['mintSymbol']} + /> + + handleSetForm({ + value: evt.target.value, + propertyName: 'mintDecimals', + }) + } + error={formErrors['mintDecimals']} + /> + + ) +} + +export default InitializeController diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index 273573f6a8..b27acbe0ba 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -36,6 +36,7 @@ import Empty from './components/instructions/Empty' import Mint from './components/instructions/Mint' import CustomBase64 from './components/instructions/CustomBase64' import { getTimestampFromDays } from '@tools/sdk/units' +import InitializeController from './components/instructions/InitializeController' const schema = yup.object().shape({ title: yup.string().required('Title is required'), }) @@ -258,6 +259,8 @@ const New = () => { return ( ) + case Instructions.InitializeController: + return case Instructions.Mint: return case Instructions.Base64: diff --git a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts new file mode 100644 index 0000000000..c80c728a50 --- /dev/null +++ b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts @@ -0,0 +1,57 @@ +import { Program, BN, utils, Provider, Wallet } from '@project-serum/anchor' +import { TOKEN_PROGRAM_ID } from '@solana/spl-token' +import { + SystemProgram, + SYSVAR_RENT_PUBKEY, + TransactionInstruction, + PublicKey, + Connection, + Keypair, +} from '@solana/web3.js' +import uxdIdl from './uxdIdl' + +const createInitializeControllerInstruction = ( + uxdProgramId: PublicKey, + mintDecimals: number, + authority: PublicKey, + connection: Connection +): TransactionInstruction => { + // generating a random wallet to be able to instantiate a dummy provider + const provider = new Provider( + connection, + new Wallet(Keypair.generate()), + Provider.defaultOptions() + ) + const program = new Program(uxdIdl, uxdProgramId, provider) + + const [pda, bump] = utils.publicKey.findProgramAddressSync( + [Buffer.from('CONTROLLER')], + uxdProgramId + ) + const [ + redeemableMintPda, + redeemableMintBump, + ] = utils.publicKey.findProgramAddressSync( + [Buffer.from('REDEEMABLE')], + uxdProgramId + ) + + return program.instruction.initializeController( + bump, + redeemableMintBump, + new BN(mintDecimals), + { + accounts: { + authority: authority, + controller: pda, + redeemableMint: redeemableMintPda, + rent: SYSVAR_RENT_PUBKEY, + systemProgram: SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + options: Provider.defaultOptions(), + } + ) +} + +export default createInitializeControllerInstruction diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 6e3790b103..e278e04b80 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -58,6 +58,23 @@ export enum Instructions { Mint, Base64, None, + InitializeController, +} + +export interface InitializeControllerForm { + governedAccount: GovernedProgramAccount | undefined + mintSymbol: string | undefined + mintDecimals: number | undefined + programId: string | undefined +} + +export enum UXDIntructions { + InitializeController, + SetRedeemableGlobalSupplyCap, + SetMangoDepositoriesRedeemableSoftCap, + RegisterMangoDepository, + DepositInsuranceToMangoDepository, + WithdrawInsuranceFromMangoDepository, } export type createParams = [ From 079c78a6df4b8e1055fb8532598949a31de6926f Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 5 Dec 2021 03:03:25 +0900 Subject: [PATCH 009/204] add SetRedeemableGlobalSupplyCap --- .../instructions/programs/uxdProtocol.tsx | 25 ++- hooks/useGovernanceAssets.ts | 5 + .../instructions/SetRedeemGlobalSupplyCap.tsx | 146 ++++++++++++++++++ pages/dao/[symbol]/proposal/new.tsx | 3 + ...SetRedeemableGlobalSupplyCapInstruction.ts | 48 ++++++ utils/uiTypes/proposalCreationTypes.ts | 7 + 6 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx create mode 100644 tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts diff --git a/components/instructions/programs/uxdProtocol.tsx b/components/instructions/programs/uxdProtocol.tsx index 4c3447ec95..3629c6fa2c 100644 --- a/components/instructions/programs/uxdProtocol.tsx +++ b/components/instructions/programs/uxdProtocol.tsx @@ -1,5 +1,5 @@ import { Connection } from '@solana/web3.js' -import { struct, u8 } from 'buffer-layout' +import { struct, u8, u48 } from 'buffer-layout' import { AccountMetaData } from '../../../models/accounts' const UXD_INIT_CONTROLLER_INSTRUCTION = { @@ -37,6 +37,29 @@ const UXD_INIT_CONTROLLER_INSTRUCTION = { }, } +const UXD_SET_REDEEMABLE_GLOBAL_SUPPLY_CAP_INSTRUCTION = { + 1: { + name: 'UXD - Set Redeemable Global Supply Cap', + accounts: ['authority', 'controller'], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) + + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, +} + export const UXD_INSTRUCTIONS = { ...UXD_INIT_CONTROLLER_INSTRUCTION, + ...UXD_SET_REDEEMABLE_GLOBAL_SUPPLY_CAP_INSTRUCTION, } diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 2a4d30e3c7..050644231d 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -60,6 +60,11 @@ export default function useGovernanceAssets() { name: 'Initialize Controller', isVisible: canUseUxdInstructions, }, + { + id: Instructions.SetRedeemableGlobalSupplyCap, + name: 'Set Redeemable Global Supply Cap', + isVisible: canUseUxdInstructions, + }, { id: Instructions.Transfer, name: 'Transfer Tokens', diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx new file mode 100644 index 0000000000..a4c7e469de --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx @@ -0,0 +1,146 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + SetRedeemableGlobalSupplyCapForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import { Governance, GovernanceAccountType } from '@models/accounts' +import { ParsedAccount } from '@models/core/accounts' +import useWalletStore from 'stores/useWalletStore' +import { serializeInstructionToBase64 } from '@models/serialisation' +import Input from '@components/inputs/Input' +import { debounce } from '@utils/debounce' +import GovernedAccountSelect from '../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import createSetRedeemableGlobalSupplyCapInstruction from '@tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction' + +const SetRedeemGlobalSupplyCap = ({ + index, + governance, +}: { + index: number + governance: ParsedAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { getGovernancesByAccountType } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountType( + GovernanceAccountType.ProgramGovernance + ).map((x) => { + return { + governance: x, + } + }) + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({ + governedAccount: undefined, + programId: programId?.toString(), + supplyCap: 0, + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + async function getInstruction(): Promise { + const isValid = await validateInstruction() + let serializedInstruction = '' + if ( + isValid && + programId && + form.governedAccount?.governance?.info && + wallet?.publicKey + ) { + const createIx = createSetRedeemableGlobalSupplyCapInstruction( + connection.current, + form.governedAccount.governance?.info.governedAccount, + form.supplyCap || 9, + form.governedAccount?.governance.pubkey + ) + serializedInstruction = serializeInstructionToBase64(createIx) + } + const obj: UiInstruction = { + serializedInstruction, + isValid, + governedAccount: form.governedAccount?.governance, + } + return obj + } + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + if (form.supplyCap) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.supplyCap]) + useEffect(() => { + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) + const schema = yup.object().shape({ + supplyCap: yup + .number() + .required('Redeemable global supply cap is required'), + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + + handleSetForm({ + value: evt.target.value, + propertyName: 'supplyCap', + }) + } + error={formErrors['global']} + /> + + ) +} + +export default SetRedeemGlobalSupplyCap diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index b27acbe0ba..bea0517b22 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -37,6 +37,7 @@ import Mint from './components/instructions/Mint' import CustomBase64 from './components/instructions/CustomBase64' import { getTimestampFromDays } from '@tools/sdk/units' import InitializeController from './components/instructions/InitializeController' +import SetRedeemGlobalSupplyCap from './components/instructions/SetRedeemGlobalSupplyCap' const schema = yup.object().shape({ title: yup.string().required('Title is required'), }) @@ -261,6 +262,8 @@ const New = () => { ) case Instructions.InitializeController: return + case Instructions.SetRedeemableGlobalSupplyCap: + return case Instructions.Mint: return case Instructions.Base64: diff --git a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts new file mode 100644 index 0000000000..7a81952e0f --- /dev/null +++ b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts @@ -0,0 +1,48 @@ +import { Program, BN, utils, Provider, Wallet } from '@project-serum/anchor' +import { + TransactionInstruction, + PublicKey, + Connection, + Keypair, +} from '@solana/web3.js' +import uxdIdl from './uxdIdl' + +const redeemableDecimals = 6 + +const createSetRedeemableGlobalSupplyCapInstruction = ( + connection: Connection, + uxdProgramId: PublicKey, + supplyCapUiAmount: number, + authority: PublicKey +): TransactionInstruction => { + // generating a random wallet to be able to instantiate a dummy provider + const provider = new Provider( + connection, + new Wallet(Keypair.generate()), + Provider.defaultOptions() + ) + + const program = new Program(uxdIdl, uxdProgramId, provider) + const [pda] = utils.publicKey.findProgramAddressSync( + [Buffer.from('CONTROLLER')], + uxdProgramId + ) + + console.log('uxdProgramId', uxdProgramId.toBase58()) + console.log('authority', authority.toBase58()) + console.log('pda', pda.toBase58()) + const decimals = new BN(10 ** redeemableDecimals) + const supplyCapNativeAmount = new BN(supplyCapUiAmount).mul(decimals) + return program.instruction.setRedeemableGlobalSupplyCap( + supplyCapNativeAmount, + { + accounts: { + authority: authority, + controller: pda, + }, + options: Provider.defaultOptions(), + } + ) +} + +export default createSetRedeemableGlobalSupplyCapInstruction diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index e278e04b80..33905c817c 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -59,6 +59,7 @@ export enum Instructions { Base64, None, InitializeController, + SetRedeemableGlobalSupplyCap, } export interface InitializeControllerForm { @@ -68,6 +69,12 @@ export interface InitializeControllerForm { programId: string | undefined } +export interface SetRedeemableGlobalSupplyCapForm { + governedAccount: GovernedProgramAccount | undefined + supplyCap: number | undefined + programId: string | undefined +} + export enum UXDIntructions { InitializeController, SetRedeemableGlobalSupplyCap, From 64326e2cf3ab0db42ad4f9d96cfcb61ce3491c44 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Mon, 6 Dec 2021 18:01:08 +0900 Subject: [PATCH 010/204] add uxdprotocol/uxd-client package --- package.json | 1 + yarn.lock | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index dd388f8647..b24c3719b6 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@solana/spl-token": "^0.1.3", "@solana/web3.js": "^1.30.2", "@tippyjs/react": "^4.2.5", + "@uxdprotocol/uxd-client": "^2.46.0", "axios": "^0.21.1", "bignumber.js": "^9.0.1", "immer": "^9.0.1", diff --git a/yarn.lock b/yarn.lock index fd6f93a996..38daed2e0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -353,6 +353,27 @@ dotenv "^10.0.0" yargs "^17.0.1" +"@blockworks-foundation/mango-client@^3.2.7": + version "3.2.14" + resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.2.14.tgz#64854e60d8bf21a571858c0385d8c699af6f639b" + integrity sha512-EBO39zx3wtX4p/BmkEznVOPvuKI8dm33zxDYRvDVLoVGDSnNMBMpRao3bgHiQpGAUp962wX9ScYmLfiZ7qdoEQ== + dependencies: + "@project-serum/anchor" "^0.16.2" + "@project-serum/serum" "0.13.55" + "@project-serum/sol-wallet-adapter" "^0.2.0" + "@solana/spl-token" "^0.1.6" + "@solana/web3.js" "1.21.0" + axios "^0.21.1" + big.js "^6.1.1" + bigint-buffer "^1.1.5" + bn.js "^5.2.0" + buffer-layout "^1.2.1" + cross-fetch "^3.1.4" + dotenv "^10.0.0" + dotenv-expand "^5.1.0" + encoding "^0.1.13" + yargs "^17.0.1" + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -977,7 +998,7 @@ buffer-layout "^1.2.0" dotenv "8.2.0" -"@solana/spl-token@^0.1.6": +"@solana/spl-token@^0.1.6", "@solana/spl-token@^0.1.8": version "0.1.8" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" integrity sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ== @@ -1393,6 +1414,16 @@ "@typescript-eslint/types" "4.33.0" eslint-visitor-keys "^2.0.0" +"@uxdprotocol/uxd-client@^2.46.0": + version "2.46.0" + resolved "https://npm.pkg.github.com/download/@uxdprotocol/uxd-client/2.46.0/8a7720c33a05508924b217d5e9b5645f5c6edbd477dbdaa9543d179aa55a44ff#27da670e96a10c0a23815c24401e8b27b6e5481e" + integrity sha512-lBliolGuPxoKKME1l1COdf0TgWyP5sMyIKrlSNOShcU2I+EdPDHWhvs4e7/cIsRs5+FNMC/XB+Ycxj3yUWfuow== + dependencies: + "@blockworks-foundation/mango-client" "^3.2.7" + "@project-serum/anchor" "^0.18.2" + "@solana/spl-token" "^0.1.8" + "@solana/web3.js" "1.30.2" + JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -2749,7 +2780,7 @@ emojis-list@^2.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= -encoding@0.1.13: +encoding@0.1.13, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== From ad885bbc98957a006a6b05e81b90c0dd8868609a Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Mon, 6 Dec 2021 18:01:20 +0900 Subject: [PATCH 011/204] add register mango depository itx --- .../instructions/programs/uxdProtocol.tsx | 37 ++++ hooks/useGovernanceAssets.ts | 5 + .../instructions/RegisterMangoDepository.tsx | 167 ++++++++++++++++++ pages/dao/[symbol]/proposal/new.tsx | 3 + ...reateRegisterMangoDepositoryInstruction.ts | 99 +++++++++++ ...SetRedeemableGlobalSupplyCapInstruction.ts | 3 - utils/uiTypes/proposalCreationTypes.ts | 8 + 7 files changed, 319 insertions(+), 3 deletions(-) create mode 100644 pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx create mode 100644 tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts diff --git a/components/instructions/programs/uxdProtocol.tsx b/components/instructions/programs/uxdProtocol.tsx index 3629c6fa2c..19a4c769a0 100644 --- a/components/instructions/programs/uxdProtocol.tsx +++ b/components/instructions/programs/uxdProtocol.tsx @@ -59,7 +59,44 @@ const UXD_SET_REDEEMABLE_GLOBAL_SUPPLY_CAP_INSTRUCTION = { }, } +const UXD_REGISTER_MANGO_DEPOSITORY_INSTRUCTION = { + 1: { + name: 'UXD - Register Mango Depository', + accounts: [ + 'authority', + 'controller', + 'depository', + 'collateralMint', // BTC/ WSOL..... + 'insuranceMint', // USDC + 'depositoryCollateralPassthroughAccount', + 'depositoryInsurancePassthroughAccount', + 'depositoryMangoAccount', + 'mangoGroup', + 'rent', + 'systemProgram', + 'tokenProgram', + 'mangoProgram', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) + + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, +} + export const UXD_INSTRUCTIONS = { ...UXD_INIT_CONTROLLER_INSTRUCTION, ...UXD_SET_REDEEMABLE_GLOBAL_SUPPLY_CAP_INSTRUCTION, + ...UXD_REGISTER_MANGO_DEPOSITORY_INSTRUCTION, } diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 050644231d..51508473dd 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -65,6 +65,11 @@ export default function useGovernanceAssets() { name: 'Set Redeemable Global Supply Cap', isVisible: canUseUxdInstructions, }, + { + id: Instructions.RegisterMangoDepository, + name: 'Register Mango Depository', + isVisible: canUseUxdInstructions, + }, { id: Instructions.Transfer, name: 'Transfer Tokens', diff --git a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx new file mode 100644 index 0000000000..e3640c320d --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx @@ -0,0 +1,167 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + RegisterMangoDepositoryForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import { Governance, GovernanceAccountType } from '@models/accounts' +import { ParsedAccount } from '@models/core/accounts' +import useWalletStore from 'stores/useWalletStore' +import { serializeInstructionToBase64 } from '@models/serialisation' +import Input from '@components/inputs/Input' +import { debounce } from '@utils/debounce' +import GovernedAccountSelect from '../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import createRegisterMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction' + +const RegisterMangoDepository = ({ + index, + governance, +}: { + index: number + governance: ParsedAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { getGovernancesByAccountType } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountType( + GovernanceAccountType.ProgramGovernance + ).map((x) => { + return { + governance: x, + } + }) + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({ + governedAccount: undefined, + programId: programId?.toString(), + collateralMint: '', + insuranceMint: '', + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + async function getInstruction(): Promise { + const isValid = await validateInstruction() + let serializedInstruction = '' + if ( + isValid && + programId && + form.governedAccount?.governance?.info && + wallet?.publicKey + ) { + const createIx = await createRegisterMangoDepositoryInstruction( + connection.current, + form.governedAccount?.governance.info.governedAccount, + form.governedAccount?.governance.pubkey, + new PublicKey(form.collateralMint), + new PublicKey(form.insuranceMint) + ) + console.log('createIx', createIx) + serializedInstruction = serializeInstructionToBase64(createIx) + } + const obj: UiInstruction = { + serializedInstruction, + isValid, + governedAccount: form.governedAccount?.governance, + } + return obj + } + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + if (form.collateralMint) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.collateralMint]) + useEffect(() => { + if (form.insuranceMint) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.insuranceMint]) + useEffect(() => { + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) + const schema = yup.object().shape({ + collateralMint: yup + .string() + .required('Collateral Mint address is required'), + insuranceMint: yup.string().required('Insurance Mint address is required'), + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + handleSetForm({ + value: evt.target.value, + propertyName: 'collateralMint', + }) + } + error={formErrors['collateralMint']} + /> + + handleSetForm({ + value: evt.target.value, + propertyName: 'insuranceMint', + }) + } + error={formErrors['insuranceMint']} + /> + + ) +} + +export default RegisterMangoDepository diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index bea0517b22..c92fb7ec7a 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -38,6 +38,7 @@ import CustomBase64 from './components/instructions/CustomBase64' import { getTimestampFromDays } from '@tools/sdk/units' import InitializeController from './components/instructions/InitializeController' import SetRedeemGlobalSupplyCap from './components/instructions/SetRedeemGlobalSupplyCap' +import RegisterMangoDepository from './components/instructions/RegisterMangoDepository' const schema = yup.object().shape({ title: yup.string().required('Title is required'), }) @@ -264,6 +265,8 @@ const New = () => { return case Instructions.SetRedeemableGlobalSupplyCap: return + case Instructions.RegisterMangoDepository: + return case Instructions.Mint: return case Instructions.Base64: diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts new file mode 100644 index 0000000000..1bdcc47158 --- /dev/null +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -0,0 +1,99 @@ +import { Program, utils, Provider, Wallet } from '@project-serum/anchor' +import { TOKEN_PROGRAM_ID } from '@solana/spl-token' +import { + SystemProgram, + SYSVAR_RENT_PUBKEY, + TransactionInstruction, + PublicKey, + Connection, + Keypair, +} from '@solana/web3.js' +import { createAndInitializeMango } from '@uxdprotocol/uxd-client' +import uxdIdl from './uxdIdl' + +const createRegisterMangoDepositoryInstruction = async ( + connection: Connection, + uxdProgramId: PublicKey, + authority: PublicKey, + collateralMint: PublicKey, + insuranceMint: PublicKey +): Promise => { + // generating a random wallet to be able to instantiate a dummy provider + const provider = new Provider( + connection, + new Wallet(Keypair.generate()), + Provider.defaultOptions() + ) + const mango = await createAndInitializeMango(provider, 'devnet') + const program = new Program(uxdIdl, uxdProgramId, provider) + const [controllerPda] = utils.publicKey.findProgramAddressSync( + [Buffer.from('CONTROLLER')], + uxdProgramId + ) + + console.log('uxdProgramId', uxdProgramId) + console.log('authority', authority) + console.log('collateralMint', collateralMint) + console.log('insuranceMint', insuranceMint) + + const [ + depositoryPda, + depositoryBump, + ] = utils.publicKey.findProgramAddressSync( + [Buffer.from('MANGODEPOSITORY'), collateralMint.toBuffer()], + uxdProgramId + ) + + const [ + collateralPassthroughPda, + collateralPassthroughBump, + ] = utils.publicKey.findProgramAddressSync( + [Buffer.from('COLLATERALPASSTHROUGH'), collateralMint.toBuffer()], + uxdProgramId + ) + const [ + insurancePassthroughPda, + insurancePassthroughBump, + ] = utils.publicKey.findProgramAddressSync( + [ + Buffer.from('INSURANCEPASSTHROUGH'), + collateralMint.toBuffer(), + insuranceMint.toBuffer(), + ], + uxdProgramId + ) + const [ + mangoAccountPda, + mangoAccountBump, + ] = utils.publicKey.findProgramAddressSync( + [Buffer.from('MANGOACCOUNT'), collateralMint.toBuffer()], + uxdProgramId + ) + + return program.instruction.registerMangoDepository( + depositoryBump, + collateralPassthroughBump, + insurancePassthroughBump, + mangoAccountBump, + { + accounts: { + authority: authority, + controller: controllerPda, + depository: depositoryPda, + collateralMint, // BTC/ WSOL..... + insuranceMint, // USDC + depositoryCollateralPassthroughAccount: collateralPassthroughPda, + depositoryInsurancePassthroughAccount: insurancePassthroughPda, + depositoryMangoAccount: mangoAccountPda, + mangoGroup: mango.group.publicKey, + rent: SYSVAR_RENT_PUBKEY, + systemProgram: SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + mangoProgram: mango.programId, + }, + options: Provider.defaultOptions(), + } + ) +} + +export default createRegisterMangoDepositoryInstruction diff --git a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts index 7a81952e0f..3914efdfe7 100644 --- a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts @@ -28,9 +28,6 @@ const createSetRedeemableGlobalSupplyCapInstruction = ( uxdProgramId ) - console.log('uxdProgramId', uxdProgramId.toBase58()) - console.log('authority', authority.toBase58()) - console.log('pda', pda.toBase58()) const decimals = new BN(10 ** redeemableDecimals) const supplyCapNativeAmount = new BN(supplyCapUiAmount).mul(decimals) return program.instruction.setRedeemableGlobalSupplyCap( diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 33905c817c..5d963e6516 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -60,6 +60,7 @@ export enum Instructions { None, InitializeController, SetRedeemableGlobalSupplyCap, + RegisterMangoDepository, } export interface InitializeControllerForm { @@ -75,6 +76,13 @@ export interface SetRedeemableGlobalSupplyCapForm { programId: string | undefined } +export interface RegisterMangoDepositoryForm { + governedAccount: GovernedProgramAccount | undefined + collateralMint: string + insuranceMint: string + programId: string | undefined +} + export enum UXDIntructions { InitializeController, SetRedeemableGlobalSupplyCap, From d47b62db7070ee394a37a8bcb46c7589e9860eb0 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 9 Dec 2021 22:55:56 +0900 Subject: [PATCH 012/204] refactor using uxdclient, update contract, tx passing --- components/instructions/programs/names.ts | 2 +- .../instructions/programs/uxdProtocol.tsx | 114 ++++++++---------- components/instructions/tools.tsx | 4 +- .../instructions/InitializeController.tsx | 2 + .../instructions/RegisterMangoDepository.tsx | 2 +- .../createInitializeControllerInstruction.ts | 29 ++--- ...reateRegisterMangoDepositoryInstruction.ts | 84 +++++-------- ...SetRedeemableGlobalSupplyCapInstruction.ts | 11 +- tools/sdk/uxdProtocol/uxdIdl.ts | 50 ++++++++ 9 files changed, 154 insertions(+), 144 deletions(-) diff --git a/components/instructions/programs/names.ts b/components/instructions/programs/names.ts index d426bb921c..701a794f74 100644 --- a/components/instructions/programs/names.ts +++ b/components/instructions/programs/names.ts @@ -21,7 +21,7 @@ export const PROGRAM_NAMES = { '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8': 'Raydium AMM Program', EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q: 'Raydium Staking Program', - FXY9qRsCxg6ioEFLu9ECQ5v2YF2qjeE1Tcnin3DUBW76: 'Controller UXD', + '73yvDSPxhtXmi8Gaheobm32n3jMdbvQzCahXgqqE12My': 'UXD Protocol Program', SysvarRent111111111111111111111111111111111: 'Sysvar: Rent', SysvarC1ock11111111111111111111111111111111: 'Sysvar: Clock', diff --git a/components/instructions/programs/uxdProtocol.tsx b/components/instructions/programs/uxdProtocol.tsx index 19a4c769a0..2d311f8c8b 100644 --- a/components/instructions/programs/uxdProtocol.tsx +++ b/components/instructions/programs/uxdProtocol.tsx @@ -2,12 +2,13 @@ import { Connection } from '@solana/web3.js' import { struct, u8, u48 } from 'buffer-layout' import { AccountMetaData } from '../../../models/accounts' -const UXD_INIT_CONTROLLER_INSTRUCTION = { - FXY9qRsCxg6ioEFLu9ECQ5v2YF2qjeE1Tcnin3DUBW76: { +export const UXD_PROGRAM_INSTRUCTIONS = { + '73yvDSPxhtXmi8Gaheobm32n3jMdbvQzCahXgqqE12My': { 1: { name: 'UXD - Initialize Controller', accounts: [ 'authority', + 'payer', 'controller', 'redeemableMint', 'systemProgram', @@ -34,69 +35,58 @@ const UXD_INIT_CONTROLLER_INSTRUCTION = { ) }, }, - }, -} - -const UXD_SET_REDEEMABLE_GLOBAL_SUPPLY_CAP_INSTRUCTION = { - 1: { - name: 'UXD - Set Redeemable Global Supply Cap', - accounts: ['authority', 'controller'], - getDataUI: ( - _connection: Connection, - data: Uint8Array, - _accounts: AccountMetaData[] - ) => { - const dataLayout = struct([u48('redeemable_global_supply_cap')]) + 2: { + name: 'UXD - Set Redeemable Global Supply Cap', + accounts: ['authority', 'controller'], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) - const args = dataLayout.decode(Buffer.from(data)) as any - console.log('args', args) - return ( - <> -

{args}

- - ) + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, }, - }, -} - -const UXD_REGISTER_MANGO_DEPOSITORY_INSTRUCTION = { - 1: { - name: 'UXD - Register Mango Depository', - accounts: [ - 'authority', - 'controller', - 'depository', - 'collateralMint', // BTC/ WSOL..... - 'insuranceMint', // USDC - 'depositoryCollateralPassthroughAccount', - 'depositoryInsurancePassthroughAccount', - 'depositoryMangoAccount', - 'mangoGroup', - 'rent', - 'systemProgram', - 'tokenProgram', - 'mangoProgram', - ], - getDataUI: ( - _connection: Connection, - data: Uint8Array, - _accounts: AccountMetaData[] - ) => { - const dataLayout = struct([u48('redeemable_global_supply_cap')]) + 3: { + name: 'UXD - Register Mango Depository', + accounts: [ + 'authority', + 'payer', + 'controller', + 'depository', + 'collateralMint', // BTC/ WSOL..... + 'insuranceMint', // USDC + 'depositoryCollateralPassthroughAccount', + 'depositoryInsurancePassthroughAccount', + 'depositoryMangoAccount', + 'mangoGroup', + 'rent', + 'systemProgram', + 'tokenProgram', + 'mangoProgram', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) - const args = dataLayout.decode(Buffer.from(data)) as any - console.log('args', args) - return ( - <> -

{args}

- - ) + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, }, }, } - -export const UXD_INSTRUCTIONS = { - ...UXD_INIT_CONTROLLER_INSTRUCTION, - ...UXD_SET_REDEEMABLE_GLOBAL_SUPPLY_CAP_INSTRUCTION, - ...UXD_REGISTER_MANGO_DEPOSITORY_INSTRUCTION, -} diff --git a/components/instructions/tools.tsx b/components/instructions/tools.tsx index ca7bb142d9..dc1f902315 100644 --- a/components/instructions/tools.tsx +++ b/components/instructions/tools.tsx @@ -7,7 +7,7 @@ import { MANGO_INSTRUCTIONS } from './programs/mango' import { getProgramName, isGovernanceProgram } from './programs/names' import { RAYDIUM_INSTRUCTIONS } from './programs/raydium' import { SPL_TOKEN_INSTRUCTIONS } from './programs/splToken' -import { UXD_INSTRUCTIONS } from './programs/uxdProtocol' +import { UXD_PROGRAM_INSTRUCTIONS } from './programs/uxdProtocol' // Well known account names displayed on the instruction card export const ACCOUNT_NAMES = { @@ -97,7 +97,7 @@ export const INSTRUCTION_DESCRIPTORS = { ...BPF_UPGRADEABLE_LOADER_INSTRUCTIONS, ...MANGO_INSTRUCTIONS, ...RAYDIUM_INSTRUCTIONS, - ...UXD_INSTRUCTIONS, + ...UXD_PROGRAM_INSTRUCTIONS, } export async function getInstructionDescriptor( diff --git a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx index f5541933c6..9034fcaa86 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx @@ -68,8 +68,10 @@ const InitializeController = ({ ) { const createIx = createInitializeControllerInstruction( form.governedAccount?.governance.info.governedAccount, + form.mintSymbol || '', form.mintDecimals || 9, form.governedAccount?.governance.pubkey, + new PublicKey(wallet.publicKey.toBase58()), connection.current ) serializedInstruction = serializeInstructionToBase64(createIx) diff --git a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx index e3640c320d..c375d5857b 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx @@ -70,10 +70,10 @@ const RegisterMangoDepository = ({ connection.current, form.governedAccount?.governance.info.governedAccount, form.governedAccount?.governance.pubkey, + new PublicKey(wallet.publicKey.toBase58()), new PublicKey(form.collateralMint), new PublicKey(form.insuranceMint) ) - console.log('createIx', createIx) serializedInstruction = serializeInstructionToBase64(createIx) } const obj: UiInstruction = { diff --git a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts index c80c728a50..7c7500fd99 100644 --- a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts +++ b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts @@ -1,4 +1,4 @@ -import { Program, BN, utils, Provider, Wallet } from '@project-serum/anchor' +import { Program, BN, Provider, Wallet } from '@project-serum/anchor' import { TOKEN_PROGRAM_ID } from '@solana/spl-token' import { SystemProgram, @@ -8,12 +8,15 @@ import { Connection, Keypair, } from '@solana/web3.js' +import { Controller } from '@uxdprotocol/uxd-client' import uxdIdl from './uxdIdl' const createInitializeControllerInstruction = ( uxdProgramId: PublicKey, + mintSymbol: string, mintDecimals: number, authority: PublicKey, + payer: PublicKey, connection: Connection ): TransactionInstruction => { // generating a random wallet to be able to instantiate a dummy provider @@ -23,28 +26,18 @@ const createInitializeControllerInstruction = ( Provider.defaultOptions() ) const program = new Program(uxdIdl, uxdProgramId, provider) - - const [pda, bump] = utils.publicKey.findProgramAddressSync( - [Buffer.from('CONTROLLER')], - uxdProgramId - ) - const [ - redeemableMintPda, - redeemableMintBump, - ] = utils.publicKey.findProgramAddressSync( - [Buffer.from('REDEEMABLE')], - uxdProgramId - ) + const controller = new Controller(mintSymbol, mintDecimals, uxdProgramId) return program.instruction.initializeController( - bump, - redeemableMintBump, + controller.bump, + controller.redeemableMintBump, new BN(mintDecimals), { accounts: { - authority: authority, - controller: pda, - redeemableMint: redeemableMintPda, + authority, + payer, + controller: controller.pda, + redeemableMint: controller.redeemableMintPda, rent: SYSVAR_RENT_PUBKEY, systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index 1bdcc47158..f46e29f93d 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -1,4 +1,4 @@ -import { Program, utils, Provider, Wallet } from '@project-serum/anchor' +import { Program, Provider, Wallet } from '@project-serum/anchor' import { TOKEN_PROGRAM_ID } from '@solana/spl-token' import { SystemProgram, @@ -8,13 +8,18 @@ import { Connection, Keypair, } from '@solana/web3.js' -import { createAndInitializeMango } from '@uxdprotocol/uxd-client' +import { + Controller, + createAndInitializeMango, + MangoDepository, +} from '@uxdprotocol/uxd-client' import uxdIdl from './uxdIdl' const createRegisterMangoDepositoryInstruction = async ( connection: Connection, uxdProgramId: PublicKey, authority: PublicKey, + payer: PublicKey, collateralMint: PublicKey, insuranceMint: PublicKey ): Promise => { @@ -26,65 +31,36 @@ const createRegisterMangoDepositoryInstruction = async ( ) const mango = await createAndInitializeMango(provider, 'devnet') const program = new Program(uxdIdl, uxdProgramId, provider) - const [controllerPda] = utils.publicKey.findProgramAddressSync( - [Buffer.from('CONTROLLER')], - uxdProgramId - ) - console.log('uxdProgramId', uxdProgramId) - console.log('authority', authority) - console.log('collateralMint', collateralMint) - console.log('insuranceMint', insuranceMint) - - const [ - depositoryPda, - depositoryBump, - ] = utils.publicKey.findProgramAddressSync( - [Buffer.from('MANGODEPOSITORY'), collateralMint.toBuffer()], - uxdProgramId - ) - - const [ - collateralPassthroughPda, - collateralPassthroughBump, - ] = utils.publicKey.findProgramAddressSync( - [Buffer.from('COLLATERALPASSTHROUGH'), collateralMint.toBuffer()], - uxdProgramId - ) - const [ - insurancePassthroughPda, - insurancePassthroughBump, - ] = utils.publicKey.findProgramAddressSync( - [ - Buffer.from('INSURANCEPASSTHROUGH'), - collateralMint.toBuffer(), - insuranceMint.toBuffer(), - ], - uxdProgramId - ) - const [ - mangoAccountPda, - mangoAccountBump, - ] = utils.publicKey.findProgramAddressSync( - [Buffer.from('MANGOACCOUNT'), collateralMint.toBuffer()], + const controller = new Controller('UXD', 6, uxdProgramId) + const depository = new MangoDepository( + collateralMint, + 'collateralName', + 6, + insuranceMint, + 'USDC', + 6, uxdProgramId ) return program.instruction.registerMangoDepository( - depositoryBump, - collateralPassthroughBump, - insurancePassthroughBump, - mangoAccountBump, + depository.bump, + depository.collateralPassthroughBump, + depository.insurancePassthroughBump, + depository.mangoAccountBump, { accounts: { - authority: authority, - controller: controllerPda, - depository: depositoryPda, - collateralMint, // BTC/ WSOL..... - insuranceMint, // USDC - depositoryCollateralPassthroughAccount: collateralPassthroughPda, - depositoryInsurancePassthroughAccount: insurancePassthroughPda, - depositoryMangoAccount: mangoAccountPda, + authority, + payer, + controller: controller.pda, + depository: depository.pda, + collateralMint: depository.collateralMint, // BTC/ WSOL..... + insuranceMint: depository.insuranceMint, // USDC + depositoryCollateralPassthroughAccount: + depository.collateralPassthroughPda, + depositoryInsurancePassthroughAccount: + depository.insurancePassthroughPda, + depositoryMangoAccount: depository.mangoAccountPda, mangoGroup: mango.group.publicKey, rent: SYSVAR_RENT_PUBKEY, systemProgram: SystemProgram.programId, diff --git a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts index 3914efdfe7..107b0e2801 100644 --- a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts @@ -1,10 +1,11 @@ -import { Program, BN, utils, Provider, Wallet } from '@project-serum/anchor' +import { Program, BN, Provider, Wallet } from '@project-serum/anchor' import { TransactionInstruction, PublicKey, Connection, Keypair, } from '@solana/web3.js' +import { Controller } from '@uxdprotocol/uxd-client' import uxdIdl from './uxdIdl' const redeemableDecimals = 6 @@ -23,19 +24,17 @@ const createSetRedeemableGlobalSupplyCapInstruction = ( ) const program = new Program(uxdIdl, uxdProgramId, provider) - const [pda] = utils.publicKey.findProgramAddressSync( - [Buffer.from('CONTROLLER')], - uxdProgramId - ) + const controller = new Controller('UXD', redeemableDecimals, uxdProgramId) const decimals = new BN(10 ** redeemableDecimals) const supplyCapNativeAmount = new BN(supplyCapUiAmount).mul(decimals) + return program.instruction.setRedeemableGlobalSupplyCap( supplyCapNativeAmount, { accounts: { authority: authority, - controller: pda, + controller: controller.pda, }, options: Provider.defaultOptions(), } diff --git a/tools/sdk/uxdProtocol/uxdIdl.ts b/tools/sdk/uxdProtocol/uxdIdl.ts index 5388476777..108db6df86 100644 --- a/tools/sdk/uxdProtocol/uxdIdl.ts +++ b/tools/sdk/uxdProtocol/uxdIdl.ts @@ -9,6 +9,11 @@ const uxdIdl: Idl = { accounts: [ { name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'payer', isMut: true, isSigner: true, }, @@ -100,6 +105,11 @@ const uxdIdl: Idl = { accounts: [ { name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'payer', isMut: true, isSigner: true, }, @@ -915,6 +925,46 @@ const uxdIdl: Idl = { name: 'InsuficientAuthorityInsuranceAmount', msg: "The Insurance ATA from authority doesn't have enough balance.", }, + { + code: 338, + name: 'InsuficentOrderBookDepth', + msg: 'Insuficcent order book depth for order.', + }, + { + code: 339, + name: 'InvalidExecutedOrderSize', + msg: 'The executed order size does not match the expected one.', + }, + { + code: 380, + name: 'MangoOrderBookLoading', + msg: 'Could not load Mango Order book.', + }, + { + code: 381, + name: 'MangoGroupLoading', + msg: 'Could not load Mango Group.', + }, + { + code: 382, + name: 'MangoCacheLoading', + msg: 'Could not load Mango Cache.', + }, + { + code: 383, + name: 'MangoLoadPerpMarket', + msg: 'Could not load Mango PerpMarket.', + }, + { + code: 384, + name: 'MangoAccountLoading', + msg: 'Could not load Mango Account.', + }, + { + code: 385, + name: 'MangoPerpMarketIndexNotFound', + msg: 'Could not find the perp market index for the given collateral.', + }, ], } From 1a3b52c80b396980dc4716512f7155d3f66bdcec Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 9 Dec 2021 23:30:03 +0900 Subject: [PATCH 013/204] add SetMangoDepositoriesRedeemableSoftCap itx --- .../instructions/programs/uxdProtocol.tsx | 19 +++ hooks/useGovernanceAssets.ts | 5 + .../SetMangoDepositoriesRedeemableSoftCap.tsx | 144 ++++++++++++++++++ pages/dao/[symbol]/proposal/new.tsx | 8 + ...teSetMangoDepositoriesRedeemableSoftCap.ts | 44 ++++++ utils/uiTypes/proposalCreationTypes.ts | 7 + 6 files changed, 227 insertions(+) create mode 100644 pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx create mode 100644 tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCap.ts diff --git a/components/instructions/programs/uxdProtocol.tsx b/components/instructions/programs/uxdProtocol.tsx index 2d311f8c8b..76f525682d 100644 --- a/components/instructions/programs/uxdProtocol.tsx +++ b/components/instructions/programs/uxdProtocol.tsx @@ -55,6 +55,25 @@ export const UXD_PROGRAM_INSTRUCTIONS = { }, }, 3: { + name: 'UXD - Set Mango Depositories Redeemable Supply Soft Cap', + accounts: ['authority', 'controller'], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) + + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, + 4: { name: 'UXD - Register Mango Depository', accounts: [ 'authority', diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 51508473dd..e3c8c9d7c9 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -65,6 +65,11 @@ export default function useGovernanceAssets() { name: 'Set Redeemable Global Supply Cap', isVisible: canUseUxdInstructions, }, + { + id: Instructions.SetMangoDepositoriesRedeemableSoftCap, + name: 'Set Mango Depositories Redeemable Supply Soft Cap', + isVisible: canUseUxdInstructions, + }, { id: Instructions.RegisterMangoDepository, name: 'Register Mango Depository', diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx new file mode 100644 index 0000000000..54b1f92821 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -0,0 +1,144 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + SetMangoDepositoriesRedeemableSoftCapForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import { Governance, GovernanceAccountType } from '@models/accounts' +import { ParsedAccount } from '@models/core/accounts' +import useWalletStore from 'stores/useWalletStore' +import { serializeInstructionToBase64 } from '@models/serialisation' +import Input from '@components/inputs/Input' +import { debounce } from '@utils/debounce' +import GovernedAccountSelect from '../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import createSetMangoDepositoriesRedeemableSoftCapInstruction from '@tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCap' + +const SetRedeemGlobalSupplyCap = ({ + index, + governance, +}: { + index: number + governance: ParsedAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { getGovernancesByAccountType } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountType( + GovernanceAccountType.ProgramGovernance + ).map((x) => { + return { + governance: x, + } + }) + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({ + governedAccount: undefined, + programId: programId?.toString(), + supplyCap: 0, + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + async function getInstruction(): Promise { + const isValid = await validateInstruction() + let serializedInstruction = '' + if ( + isValid && + programId && + form.governedAccount?.governance?.info && + wallet?.publicKey + ) { + const createIx = createSetMangoDepositoriesRedeemableSoftCapInstruction( + connection.current, + form.governedAccount.governance?.info.governedAccount, + form.supplyCap || 9, + form.governedAccount?.governance.pubkey + ) + serializedInstruction = serializeInstructionToBase64(createIx) + } + const obj: UiInstruction = { + serializedInstruction, + isValid, + governedAccount: form.governedAccount?.governance, + } + return obj + } + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + if (form.supplyCap) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.supplyCap]) + useEffect(() => { + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) + const schema = yup.object().shape({ + supplyCap: yup.number().required('Redeemable supply cap is required'), + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + + handleSetForm({ + value: evt.target.value, + propertyName: 'supplyCap', + }) + } + error={formErrors['global']} + /> + + ) +} + +export default SetRedeemGlobalSupplyCap diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index c92fb7ec7a..4b5eef903b 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -39,6 +39,7 @@ import { getTimestampFromDays } from '@tools/sdk/units' import InitializeController from './components/instructions/InitializeController' import SetRedeemGlobalSupplyCap from './components/instructions/SetRedeemGlobalSupplyCap' import RegisterMangoDepository from './components/instructions/RegisterMangoDepository' +import SetMangoDepositoriesRedeemableSoftCap from './components/instructions/SetMangoDepositoriesRedeemableSoftCap' const schema = yup.object().shape({ title: yup.string().required('Title is required'), }) @@ -265,6 +266,13 @@ const New = () => { return case Instructions.SetRedeemableGlobalSupplyCap: return + case Instructions.SetMangoDepositoriesRedeemableSoftCap: + return ( + + ) case Instructions.RegisterMangoDepository: return case Instructions.Mint: diff --git a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCap.ts b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCap.ts new file mode 100644 index 0000000000..0abd12482f --- /dev/null +++ b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCap.ts @@ -0,0 +1,44 @@ +import { Program, BN, Provider, Wallet } from '@project-serum/anchor' +import { + TransactionInstruction, + PublicKey, + Connection, + Keypair, +} from '@solana/web3.js' +import { Controller } from '@uxdprotocol/uxd-client' +import uxdIdl from './uxdIdl' + +const redeemableDecimals = 6 + +const createSetMangoDepositoriesRedeemableSoftCapInstruction = ( + connection: Connection, + uxdProgramId: PublicKey, + supplyCapUiAmount: number, + authority: PublicKey +): TransactionInstruction => { + // generating a random wallet to be able to instantiate a dummy provider + const provider = new Provider( + connection, + new Wallet(Keypair.generate()), + Provider.defaultOptions() + ) + + const program = new Program(uxdIdl, uxdProgramId, provider) + const controller = new Controller('UXD', redeemableDecimals, uxdProgramId) + + const decimals = new BN(10 ** redeemableDecimals) + const supplyCapNativeAmount = new BN(supplyCapUiAmount).mul(decimals) + + return program.instruction.setMangoDepositoriesRedeemableSoftCap( + supplyCapNativeAmount, + { + accounts: { + authority: authority, + controller: controller.pda, + }, + options: Provider.defaultOptions(), + } + ) +} + +export default createSetMangoDepositoriesRedeemableSoftCapInstruction diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 5d963e6516..6a8233c186 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -60,6 +60,7 @@ export enum Instructions { None, InitializeController, SetRedeemableGlobalSupplyCap, + SetMangoDepositoriesRedeemableSoftCap, RegisterMangoDepository, } @@ -76,6 +77,12 @@ export interface SetRedeemableGlobalSupplyCapForm { programId: string | undefined } +export interface SetMangoDepositoriesRedeemableSoftCapForm { + governedAccount: GovernedProgramAccount | undefined + supplyCap: number | undefined + programId: string | undefined +} + export interface RegisterMangoDepositoryForm { governedAccount: GovernedProgramAccount | undefined collateralMint: string From d48243e05bbb82675e303da9cb92e1d83f364f43 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 9 Dec 2021 23:45:31 +0900 Subject: [PATCH 014/204] fix naming --- .../instructions/SetMangoDepositoriesRedeemableSoftCap.tsx | 2 +- ...> createSetMangoDepositoriesRedeemableSoftCapInstruction.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tools/sdk/uxdProtocol/{createSetMangoDepositoriesRedeemableSoftCap.ts => createSetMangoDepositoriesRedeemableSoftCapInstruction.ts} (100%) diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx index 54b1f92821..500f480143 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -18,7 +18,7 @@ import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' import GovernedAccountSelect from '../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' -import createSetMangoDepositoriesRedeemableSoftCapInstruction from '@tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCap' +import createSetMangoDepositoriesRedeemableSoftCapInstruction from '@tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction' const SetRedeemGlobalSupplyCap = ({ index, diff --git a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCap.ts b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts similarity index 100% rename from tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCap.ts rename to tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts From 18dd8285e3db0ae37efae34ae2dbb117dd43656f Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 10 Dec 2021 15:18:18 +0900 Subject: [PATCH 015/204] wip - add DepositInsuranceToMangoDepository --- .../instructions/programs/uxdProtocol.tsx | 36 ++++ hooks/useGovernanceAssets.ts | 5 + .../DepositInsuranceToMangoDepository.tsx | 182 ++++++++++++++++++ .../SetMangoDepositoriesRedeemableSoftCap.tsx | 4 +- .../instructions/SetRedeemGlobalSupplyCap.tsx | 2 +- pages/dao/[symbol]/proposal/new.tsx | 8 + ...itInsuranceToMangoDepositoryInstruction.ts | 131 +++++++++++++ utils/uiTypes/proposalCreationTypes.ts | 9 + 8 files changed, 374 insertions(+), 3 deletions(-) create mode 100644 pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx create mode 100644 tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts diff --git a/components/instructions/programs/uxdProtocol.tsx b/components/instructions/programs/uxdProtocol.tsx index 76f525682d..bbeafd9153 100644 --- a/components/instructions/programs/uxdProtocol.tsx +++ b/components/instructions/programs/uxdProtocol.tsx @@ -98,6 +98,42 @@ export const UXD_PROGRAM_INSTRUCTIONS = { ) => { const dataLayout = struct([u48('redeemable_global_supply_cap')]) + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, + 5: { + name: 'UXD - Deposit Insurance To Mango Depository', + accounts: [ + 'authority', + 'controller', + 'depository', + 'insuranceMint', + 'authorityInsurance', + 'depositoryInsurancePassthroughAccount', + 'depositoryMangoAccount', + // mango accounts for CPI + 'mangoGroup', + 'mangoCache', + 'mangoRootBank', + 'mangoNodeBank', + 'mangoVault', + // + 'tokenProgram', + 'mangoProgram', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) + const args = dataLayout.decode(Buffer.from(data)) as any console.log('args', args) return ( diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index e3c8c9d7c9..bc17c7dc22 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -75,6 +75,11 @@ export default function useGovernanceAssets() { name: 'Register Mango Depository', isVisible: canUseUxdInstructions, }, + { + id: Instructions.DepositInsuranceToMangoDepository, + name: 'Deposit Insurance To Mango Depository', + isVisible: canUseUxdInstructions, + }, { id: Instructions.Transfer, name: 'Transfer Tokens', diff --git a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx new file mode 100644 index 0000000000..d2df913665 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx @@ -0,0 +1,182 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + DepositInsuranceToMangoDepositoryForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import { Governance, GovernanceAccountType } from '@models/accounts' +import { ParsedAccount } from '@models/core/accounts' +import useWalletStore from 'stores/useWalletStore' +import { serializeInstructionToBase64 } from '@models/serialisation' +import Input from '@components/inputs/Input' +import { debounce } from '@utils/debounce' +import GovernedAccountSelect from '../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import createDepositInsuranceToMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction' + +const DepositInsuranceToMangoDepository = ({ + index, + governance, +}: { + index: number + governance: ParsedAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { getGovernancesByAccountType } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountType( + GovernanceAccountType.ProgramGovernance + ).map((x) => { + return { + governance: x, + } + }) + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({ + governedAccount: undefined, + programId: programId?.toString(), + collateralMint: '', + insuranceMint: '', + insuranceDepositedAmount: 0, + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + async function getInstruction(): Promise { + const isValid = await validateInstruction() + let serializedInstruction = '' + if ( + isValid && + programId && + form.governedAccount?.governance?.info && + wallet?.publicKey + ) { + const createIx = await createDepositInsuranceToMangoDepositoryInstruction( + connection.current, + form.governedAccount?.governance.info.governedAccount, + form.governedAccount?.governance.pubkey, + new PublicKey(form.collateralMint), + new PublicKey(form.insuranceMint), + form.insuranceDepositedAmount || 0 + ) + serializedInstruction = serializeInstructionToBase64(createIx) + } + const obj: UiInstruction = { + serializedInstruction, + isValid, + governedAccount: form.governedAccount?.governance, + } + return obj + } + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + if (form.collateralMint) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.collateralMint]) + useEffect(() => { + if (form.insuranceMint) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.insuranceMint]) + useEffect(() => { + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) + const schema = yup.object().shape({ + collateralMint: yup + .string() + .required('Collateral Mint address is required'), + insuranceMint: yup.string().required('Insurance Mint address is required'), + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + handleSetForm({ + value: evt.target.value, + propertyName: 'collateralMint', + }) + } + error={formErrors['collateralMint']} + /> + + handleSetForm({ + value: evt.target.value, + propertyName: 'insuranceMint', + }) + } + error={formErrors['insuranceMint']} + /> + + handleSetForm({ + value: evt.target.value, + propertyName: 'insuranceDepositedAmount', + }) + } + error={formErrors['global']} + /> + + ) +} + +export default DepositInsuranceToMangoDepository diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx index 500f480143..cf69ca53d9 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -20,7 +20,7 @@ import GovernedAccountSelect from '../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createSetMangoDepositoriesRedeemableSoftCapInstruction from '@tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction' -const SetRedeemGlobalSupplyCap = ({ +const SetMangoDepositoriesRedeemableSoftCap = ({ index, governance, }: { @@ -141,4 +141,4 @@ const SetRedeemGlobalSupplyCap = ({ ) } -export default SetRedeemGlobalSupplyCap +export default SetMangoDepositoriesRedeemableSoftCap diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx index a4c7e469de..ee5b689666 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx @@ -68,7 +68,7 @@ const SetRedeemGlobalSupplyCap = ({ const createIx = createSetRedeemableGlobalSupplyCapInstruction( connection.current, form.governedAccount.governance?.info.governedAccount, - form.supplyCap || 9, + form.supplyCap || 0, form.governedAccount?.governance.pubkey ) serializedInstruction = serializeInstructionToBase64(createIx) diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index 4b5eef903b..fdb90a2965 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -40,6 +40,7 @@ import InitializeController from './components/instructions/InitializeController import SetRedeemGlobalSupplyCap from './components/instructions/SetRedeemGlobalSupplyCap' import RegisterMangoDepository from './components/instructions/RegisterMangoDepository' import SetMangoDepositoriesRedeemableSoftCap from './components/instructions/SetMangoDepositoriesRedeemableSoftCap' +import DepositInsuranceToMangoDepository from './components/instructions/DepositInsuranceToMangoDepository' const schema = yup.object().shape({ title: yup.string().required('Title is required'), }) @@ -275,6 +276,13 @@ const New = () => { ) case Instructions.RegisterMangoDepository: return + case Instructions.DepositInsuranceToMangoDepository: + return ( + + ) case Instructions.Mint: return case Instructions.Base64: diff --git a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts new file mode 100644 index 0000000000..2741e3f3db --- /dev/null +++ b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts @@ -0,0 +1,131 @@ +import { Program, BN, Provider, Wallet, utils } from '@project-serum/anchor' +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token' +import { + TransactionInstruction, + PublicKey, + Connection, + Keypair, +} from '@solana/web3.js' +import { + Controller, + createAndInitializeMango, + MangoDepository, +} from '@uxdprotocol/uxd-client' +import uxdIdl from './uxdIdl' + +// derives the canonical token account address for a given wallet and mint +function findAssociatedTokenAddress( + walletKey: PublicKey, + mintKey: PublicKey +): PublicKey { + return findAddr( + [walletKey.toBytes(), TOKEN_PROGRAM_ID.toBytes(), mintKey.toBytes()], + ASSOCIATED_TOKEN_PROGRAM_ID + ) +} + +// simple shorthand +function findAddr( + seeds: (Buffer | Uint8Array)[], + programId: PublicKey +): PublicKey { + return utils.publicKey.findProgramAddressSync(seeds, programId)[0] +} + +const createDepositInsuranceToMangoDepositoryInstruction = async ( + connection: Connection, + uxdProgramId: PublicKey, + authority: PublicKey, + depositoryMint: PublicKey, + insuranceMint: PublicKey, + insuranceDepositedAmount: number +): Promise => { + // generating a random wallet to be able to instantiate a dummy provider + const provider = new Provider( + connection, + new Wallet(Keypair.generate()), + Provider.defaultOptions() + ) + const mango = await createAndInitializeMango(provider, 'devnet') + const program = new Program(uxdIdl, uxdProgramId, provider) + + const controller = new Controller('UXD', 6, uxdProgramId) + const depository = new MangoDepository( + depositoryMint, + 'collateralName', + 6, + insuranceMint, + 'USDC', + 6, + uxdProgramId + ) + + const depositedTokenIndex = mango.group.getTokenIndex( + depository.insuranceMint + ) + const mangoCacheAccount = mango.getMangoCacheAccount() + const mangoRootBankAccount = mango.getRootBankForToken(depositedTokenIndex) + const mangoNodeBankAccount = mango.getNodeBankFor( + depositedTokenIndex, + depository.insuranceMint + ) + const mangoDepositedVaultAccount = mango.getVaultFor(depositedTokenIndex) + const authorityInsuranceATA = findAssociatedTokenAddress( + authority, + depository.insuranceMint + ) + const insuranceAmountBN = new BN( + insuranceDepositedAmount * 10 ** depository.insuranceMintdecimals + ) + + console.log({ + authority: authority.toBase58(), + controller: controller.pda.toBase58(), + depository: depository.pda.toBase58(), + collateralMint: depository.collateralMint.toBase58(), + insuranceMint: depository.insuranceMint.toBase58(), + authorityInsurance: authorityInsuranceATA.toBase58(), + depositoryInsurancePassthroughAccount: depository.insurancePassthroughPda.toBase58(), + depositoryMangoAccount: depository.mangoAccountPda.toBase58(), + // mango accounts for CPI + mangoGroup: mango.group.publicKey.toBase58(), + mangoCache: mangoCacheAccount.toBase58(), + mangoRootBank: mangoRootBankAccount.toBase58(), + mangoNodeBank: mangoNodeBankAccount.toBase58(), + mangoVault: mangoDepositedVaultAccount.toBase58(), + // + tokenProgram: TOKEN_PROGRAM_ID.toBase58(), + mangoProgram: mango.programId.toBase58(), + }) + return program.instruction.depositInsuranceToMangoDepository( + insuranceAmountBN, + { + accounts: { + authority: authority, + controller: controller.pda, + depository: depository.pda, + collateralMint: depository.collateralMint, + insuranceMint: depository.insuranceMint, + authorityInsurance: authorityInsuranceATA, + depositoryInsurancePassthroughAccount: + depository.insurancePassthroughPda, + depositoryMangoAccount: depository.mangoAccountPda, + // mango accounts for CPI + mangoGroup: mango.group.publicKey, + mangoCache: mangoCacheAccount, + mangoRootBank: mangoRootBankAccount, + mangoNodeBank: mangoNodeBankAccount, + mangoVault: mangoDepositedVaultAccount, + // + tokenProgram: TOKEN_PROGRAM_ID, + mangoProgram: mango.programId, + }, + options: Provider.defaultOptions(), + } + ) +} + +export default createDepositInsuranceToMangoDepositoryInstruction diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 6a8233c186..83c230a2ba 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -62,6 +62,7 @@ export enum Instructions { SetRedeemableGlobalSupplyCap, SetMangoDepositoriesRedeemableSoftCap, RegisterMangoDepository, + DepositInsuranceToMangoDepository, } export interface InitializeControllerForm { @@ -90,6 +91,14 @@ export interface RegisterMangoDepositoryForm { programId: string | undefined } +export interface DepositInsuranceToMangoDepositoryForm { + governedAccount: GovernedProgramAccount | undefined + collateralMint: string + insuranceMint: string + insuranceDepositedAmount: number | undefined + programId: string | undefined +} + export enum UXDIntructions { InitializeController, SetRedeemableGlobalSupplyCap, From ab1cea397f0f5fdea3da28dbefde6fc261de8b59 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sat, 11 Dec 2021 00:19:47 +0900 Subject: [PATCH 016/204] refactor added uxdClient fct --- package.json | 10 +- ...itInsuranceToMangoDepositoryInstruction.ts | 118 ++---------------- .../createInitializeControllerInstruction.ts | 45 ++----- ...reateRegisterMangoDepositoryInstruction.ts | 65 ++-------- ...epositoriesRedeemableSoftCapInstruction.ts | 41 ++---- ...SetRedeemableGlobalSupplyCapInstruction.ts | 40 ++---- tools/sdk/uxdProtocol/uxdClient.ts | 30 +++++ yarn.lock | 8 +- 8 files changed, 90 insertions(+), 267 deletions(-) create mode 100644 tools/sdk/uxdProtocol/uxdClient.ts diff --git a/package.json b/package.json index b24c3719b6..ed2ea517ff 100644 --- a/package.json +++ b/package.json @@ -22,19 +22,19 @@ ] }, "dependencies": { - "@blockworks-foundation/mango-client": "^3.2.2", + "@blockworks-foundation/mango-client": "^3.2.15", + "@project-serum/anchor": "^0.19.0", + "@solana/spl-token": "^0.1.8", + "@solana/web3.js": "1.31.0", "@emotion/react": "^11.1.5", "@emotion/styled": "^11.3.0", "@headlessui/react": "^1.0.0", "@heroicons/react": "^1.0.1", - "@project-serum/anchor": "^0.18.2", "@project-serum/borsh": "^0.2.2", "@project-serum/common": "^0.0.1-beta.3", "@project-serum/sol-wallet-adapter": "^0.2.0", - "@solana/spl-token": "^0.1.3", - "@solana/web3.js": "^1.30.2", "@tippyjs/react": "^4.2.5", - "@uxdprotocol/uxd-client": "^2.46.0", + "@uxdprotocol/uxd-client": "^2.56.0", "axios": "^0.21.1", "bignumber.js": "^9.0.1", "immer": "^9.0.1", diff --git a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts index 2741e3f3db..4e3b65a9bd 100644 --- a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts @@ -1,39 +1,7 @@ -import { Program, BN, Provider, Wallet, utils } from '@project-serum/anchor' -import { - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, -} from '@solana/spl-token' -import { - TransactionInstruction, - PublicKey, - Connection, - Keypair, -} from '@solana/web3.js' -import { - Controller, - createAndInitializeMango, - MangoDepository, -} from '@uxdprotocol/uxd-client' -import uxdIdl from './uxdIdl' - -// derives the canonical token account address for a given wallet and mint -function findAssociatedTokenAddress( - walletKey: PublicKey, - mintKey: PublicKey -): PublicKey { - return findAddr( - [walletKey.toBytes(), TOKEN_PROGRAM_ID.toBytes(), mintKey.toBytes()], - ASSOCIATED_TOKEN_PROGRAM_ID - ) -} - -// simple shorthand -function findAddr( - seeds: (Buffer | Uint8Array)[], - programId: PublicKey -): PublicKey { - return utils.publicKey.findProgramAddressSync(seeds, programId)[0] -} +import { Provider } from '@project-serum/anchor' +import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { MangoDepository } from '@uxdprotocol/uxd-client' +import { uxdClient, initializeMango } from './uxdClient' const createDepositInsuranceToMangoDepositoryInstruction = async ( connection: Connection, @@ -43,16 +11,9 @@ const createDepositInsuranceToMangoDepositoryInstruction = async ( insuranceMint: PublicKey, insuranceDepositedAmount: number ): Promise => { - // generating a random wallet to be able to instantiate a dummy provider - const provider = new Provider( - connection, - new Wallet(Keypair.generate()), - Provider.defaultOptions() - ) - const mango = await createAndInitializeMango(provider, 'devnet') - const program = new Program(uxdIdl, uxdProgramId, provider) + const { client, controller } = uxdClient(connection, uxdProgramId) - const controller = new Controller('UXD', 6, uxdProgramId) + const mango = await initializeMango(connection) const depository = new MangoDepository( depositoryMint, 'collateralName', @@ -63,68 +24,13 @@ const createDepositInsuranceToMangoDepositoryInstruction = async ( uxdProgramId ) - const depositedTokenIndex = mango.group.getTokenIndex( - depository.insuranceMint - ) - const mangoCacheAccount = mango.getMangoCacheAccount() - const mangoRootBankAccount = mango.getRootBankForToken(depositedTokenIndex) - const mangoNodeBankAccount = mango.getNodeBankFor( - depositedTokenIndex, - depository.insuranceMint - ) - const mangoDepositedVaultAccount = mango.getVaultFor(depositedTokenIndex) - const authorityInsuranceATA = findAssociatedTokenAddress( + return client.createDepositInsuranceToMangoDepositoryInstruction( + insuranceDepositedAmount, + controller, + depository, + mango, authority, - depository.insuranceMint - ) - const insuranceAmountBN = new BN( - insuranceDepositedAmount * 10 ** depository.insuranceMintdecimals - ) - - console.log({ - authority: authority.toBase58(), - controller: controller.pda.toBase58(), - depository: depository.pda.toBase58(), - collateralMint: depository.collateralMint.toBase58(), - insuranceMint: depository.insuranceMint.toBase58(), - authorityInsurance: authorityInsuranceATA.toBase58(), - depositoryInsurancePassthroughAccount: depository.insurancePassthroughPda.toBase58(), - depositoryMangoAccount: depository.mangoAccountPda.toBase58(), - // mango accounts for CPI - mangoGroup: mango.group.publicKey.toBase58(), - mangoCache: mangoCacheAccount.toBase58(), - mangoRootBank: mangoRootBankAccount.toBase58(), - mangoNodeBank: mangoNodeBankAccount.toBase58(), - mangoVault: mangoDepositedVaultAccount.toBase58(), - // - tokenProgram: TOKEN_PROGRAM_ID.toBase58(), - mangoProgram: mango.programId.toBase58(), - }) - return program.instruction.depositInsuranceToMangoDepository( - insuranceAmountBN, - { - accounts: { - authority: authority, - controller: controller.pda, - depository: depository.pda, - collateralMint: depository.collateralMint, - insuranceMint: depository.insuranceMint, - authorityInsurance: authorityInsuranceATA, - depositoryInsurancePassthroughAccount: - depository.insurancePassthroughPda, - depositoryMangoAccount: depository.mangoAccountPda, - // mango accounts for CPI - mangoGroup: mango.group.publicKey, - mangoCache: mangoCacheAccount, - mangoRootBank: mangoRootBankAccount, - mangoNodeBank: mangoNodeBankAccount, - mangoVault: mangoDepositedVaultAccount, - // - tokenProgram: TOKEN_PROGRAM_ID, - mangoProgram: mango.programId, - }, - options: Provider.defaultOptions(), - } + Provider.defaultOptions() ) } diff --git a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts index 7c7500fd99..a0a49ffd4b 100644 --- a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts +++ b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts @@ -1,15 +1,6 @@ -import { Program, BN, Provider, Wallet } from '@project-serum/anchor' -import { TOKEN_PROGRAM_ID } from '@solana/spl-token' -import { - SystemProgram, - SYSVAR_RENT_PUBKEY, - TransactionInstruction, - PublicKey, - Connection, - Keypair, -} from '@solana/web3.js' -import { Controller } from '@uxdprotocol/uxd-client' -import uxdIdl from './uxdIdl' +import { Provider } from '@project-serum/anchor' +import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { uxdClient } from './uxdClient' const createInitializeControllerInstruction = ( uxdProgramId: PublicKey, @@ -19,31 +10,13 @@ const createInitializeControllerInstruction = ( payer: PublicKey, connection: Connection ): TransactionInstruction => { - // generating a random wallet to be able to instantiate a dummy provider - const provider = new Provider( - connection, - new Wallet(Keypair.generate()), - Provider.defaultOptions() - ) - const program = new Program(uxdIdl, uxdProgramId, provider) - const controller = new Controller(mintSymbol, mintDecimals, uxdProgramId) + const { client, controller } = uxdClient(connection, uxdProgramId) - return program.instruction.initializeController( - controller.bump, - controller.redeemableMintBump, - new BN(mintDecimals), - { - accounts: { - authority, - payer, - controller: controller.pda, - redeemableMint: controller.redeemableMintPda, - rent: SYSVAR_RENT_PUBKEY, - systemProgram: SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - options: Provider.defaultOptions(), - } + return client.createInitializeControllerInstruction( + controller, + authority, + Provider.defaultOptions(), + payer ) } diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index f46e29f93d..6512ba5a4c 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -1,19 +1,7 @@ -import { Program, Provider, Wallet } from '@project-serum/anchor' -import { TOKEN_PROGRAM_ID } from '@solana/spl-token' -import { - SystemProgram, - SYSVAR_RENT_PUBKEY, - TransactionInstruction, - PublicKey, - Connection, - Keypair, -} from '@solana/web3.js' -import { - Controller, - createAndInitializeMango, - MangoDepository, -} from '@uxdprotocol/uxd-client' -import uxdIdl from './uxdIdl' +import { Provider } from '@project-serum/anchor' +import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { MangoDepository } from '@uxdprotocol/uxd-client' +import { initializeMango, uxdClient } from './uxdClient' const createRegisterMangoDepositoryInstruction = async ( connection: Connection, @@ -23,16 +11,7 @@ const createRegisterMangoDepositoryInstruction = async ( collateralMint: PublicKey, insuranceMint: PublicKey ): Promise => { - // generating a random wallet to be able to instantiate a dummy provider - const provider = new Provider( - connection, - new Wallet(Keypair.generate()), - Provider.defaultOptions() - ) - const mango = await createAndInitializeMango(provider, 'devnet') - const program = new Program(uxdIdl, uxdProgramId, provider) - - const controller = new Controller('UXD', 6, uxdProgramId) + const mango = await initializeMango(connection) const depository = new MangoDepository( collateralMint, 'collateralName', @@ -43,32 +22,14 @@ const createRegisterMangoDepositoryInstruction = async ( uxdProgramId ) - return program.instruction.registerMangoDepository( - depository.bump, - depository.collateralPassthroughBump, - depository.insurancePassthroughBump, - depository.mangoAccountBump, - { - accounts: { - authority, - payer, - controller: controller.pda, - depository: depository.pda, - collateralMint: depository.collateralMint, // BTC/ WSOL..... - insuranceMint: depository.insuranceMint, // USDC - depositoryCollateralPassthroughAccount: - depository.collateralPassthroughPda, - depositoryInsurancePassthroughAccount: - depository.insurancePassthroughPda, - depositoryMangoAccount: depository.mangoAccountPda, - mangoGroup: mango.group.publicKey, - rent: SYSVAR_RENT_PUBKEY, - systemProgram: SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - mangoProgram: mango.programId, - }, - options: Provider.defaultOptions(), - } + const { client, controller } = uxdClient(connection, uxdProgramId) + return client.createRegisterMangoDepositoryInstruction( + controller, + depository, + mango, + authority, + Provider.defaultOptions(), + payer ) } diff --git a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts index 0abd12482f..be1e02b25c 100644 --- a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts @@ -1,14 +1,6 @@ -import { Program, BN, Provider, Wallet } from '@project-serum/anchor' -import { - TransactionInstruction, - PublicKey, - Connection, - Keypair, -} from '@solana/web3.js' -import { Controller } from '@uxdprotocol/uxd-client' -import uxdIdl from './uxdIdl' - -const redeemableDecimals = 6 +import { Provider } from '@project-serum/anchor' +import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { uxdClient } from './uxdClient' const createSetMangoDepositoriesRedeemableSoftCapInstruction = ( connection: Connection, @@ -16,28 +8,13 @@ const createSetMangoDepositoriesRedeemableSoftCapInstruction = ( supplyCapUiAmount: number, authority: PublicKey ): TransactionInstruction => { - // generating a random wallet to be able to instantiate a dummy provider - const provider = new Provider( - connection, - new Wallet(Keypair.generate()), - Provider.defaultOptions() - ) + const { client, controller } = uxdClient(connection, uxdProgramId) - const program = new Program(uxdIdl, uxdProgramId, provider) - const controller = new Controller('UXD', redeemableDecimals, uxdProgramId) - - const decimals = new BN(10 ** redeemableDecimals) - const supplyCapNativeAmount = new BN(supplyCapUiAmount).mul(decimals) - - return program.instruction.setMangoDepositoriesRedeemableSoftCap( - supplyCapNativeAmount, - { - accounts: { - authority: authority, - controller: controller.pda, - }, - options: Provider.defaultOptions(), - } + return client.createSetMangoDepositoriesRedeemableSoftCapInstruction( + controller, + authority, + supplyCapUiAmount, + Provider.defaultOptions() ) } diff --git a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts index 107b0e2801..66f183e46f 100644 --- a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts @@ -1,14 +1,6 @@ -import { Program, BN, Provider, Wallet } from '@project-serum/anchor' -import { - TransactionInstruction, - PublicKey, - Connection, - Keypair, -} from '@solana/web3.js' -import { Controller } from '@uxdprotocol/uxd-client' -import uxdIdl from './uxdIdl' - -const redeemableDecimals = 6 +import { Provider } from '@project-serum/anchor' +import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { uxdClient } from './uxdClient' const createSetRedeemableGlobalSupplyCapInstruction = ( connection: Connection, @@ -16,29 +8,13 @@ const createSetRedeemableGlobalSupplyCapInstruction = ( supplyCapUiAmount: number, authority: PublicKey ): TransactionInstruction => { - // generating a random wallet to be able to instantiate a dummy provider - const provider = new Provider( - connection, - new Wallet(Keypair.generate()), + const { client, controller } = uxdClient(connection, uxdProgramId) + return client.createSetRedeemableGlobalSupplyCapInstruction( + controller, + authority, + supplyCapUiAmount, Provider.defaultOptions() ) - - const program = new Program(uxdIdl, uxdProgramId, provider) - const controller = new Controller('UXD', redeemableDecimals, uxdProgramId) - - const decimals = new BN(10 ** redeemableDecimals) - const supplyCapNativeAmount = new BN(supplyCapUiAmount).mul(decimals) - - return program.instruction.setRedeemableGlobalSupplyCap( - supplyCapNativeAmount, - { - accounts: { - authority: authority, - controller: controller.pda, - }, - options: Provider.defaultOptions(), - } - ) } export default createSetRedeemableGlobalSupplyCapInstruction diff --git a/tools/sdk/uxdProtocol/uxdClient.ts b/tools/sdk/uxdProtocol/uxdClient.ts new file mode 100644 index 0000000000..96125c9229 --- /dev/null +++ b/tools/sdk/uxdProtocol/uxdClient.ts @@ -0,0 +1,30 @@ +import { Program, Provider } from '@project-serum/anchor' +import Wallet from '@project-serum/sol-wallet-adapter' +import { Connection, PublicKey } from '@solana/web3.js' +import { + Controller, + createAndInitializeMango, + UXD, +} from '@uxdprotocol/uxd-client' +import uxdIdl from './uxdIdl' + +const DEFAULT_WALLET_PROVIDER = 'https://sollet.io' +const CLUSTER = 'devnet' + +export const uxdClient = ( + connection: Connection, + programId: PublicKey +): { client: UXD; controller: Controller } => { + const wallet = new Wallet(DEFAULT_WALLET_PROVIDER) + const provider = new Provider(connection, wallet, Provider.defaultOptions()) + return { + client: new UXD(new Program(uxdIdl, programId, provider)), + controller: new Controller('UXD', 6, programId), + } +} + +export const initializeMango = async (connection: Connection) => { + const wallet = new Wallet(DEFAULT_WALLET_PROVIDER) + const provider = new Provider(connection, wallet, Provider.defaultOptions()) + return createAndInitializeMango(provider, CLUSTER) +} diff --git a/yarn.lock b/yarn.lock index 38daed2e0f..37ebf0f6cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1030,10 +1030,10 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0": - version "1.31.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.31.0.tgz#7a313d4c1a90b77f27ddbfe845a10d6883e06452" - integrity sha512-7nHHx1JNFnrt15e9y8m38I/EJCbaB+bFC3KZVM1+QhybCikFxGMtGA5r7PDC3GEL1R2RZA8yKoLkDKo3vzzqnw== +"@solana/web3.js@1.30.2", "@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.30.2": + version "1.30.2" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.30.2.tgz#e85da75e0825dc64f53eb64a1ff0115b27bec135" + integrity sha512-hznCj+rkfvM5taRP3Z+l5lumB7IQnDrB4l55Wpsg4kDU9Zds8pE5YOH5Z9bbF/pUzZJKQjyBjnY/6kScBm3Ugg== dependencies: "@babel/runtime" "^7.12.5" "@ethersproject/sha2" "^5.5.0" From 9cce40c5a70a5f6b67ffed3e535e74466e3a62d7 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 12 Dec 2021 10:01:08 +0900 Subject: [PATCH 017/204] wip add WithdrawInsuranceFromMangoDepository itx --- .../instructions/programs/uxdProtocol.tsx | 36 ++++ hooks/useGovernanceAssets.ts | 5 + .../WithdrawInsuranceFromMangoDepository.tsx | 182 ++++++++++++++++++ pages/dao/[symbol]/proposal/new.tsx | 8 + ...InsuranceFromMangoDepositoryInstruction.ts | 37 ++++ utils/uiTypes/proposalCreationTypes.ts | 9 + 6 files changed, 277 insertions(+) create mode 100644 pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx create mode 100644 tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts diff --git a/components/instructions/programs/uxdProtocol.tsx b/components/instructions/programs/uxdProtocol.tsx index bbeafd9153..14f55786b2 100644 --- a/components/instructions/programs/uxdProtocol.tsx +++ b/components/instructions/programs/uxdProtocol.tsx @@ -134,6 +134,42 @@ export const UXD_PROGRAM_INSTRUCTIONS = { ) => { const dataLayout = struct([u48('redeemable_global_supply_cap')]) + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, + 6: { + name: 'UXD - Withdraw Insurance From Mango Depository', + accounts: [ + 'authority', + 'controller', + 'depository', + 'insuranceMint', + 'authorityInsurance', + 'depositoryInsurancePassthroughAccount', + 'depositoryMangoAccount', + // mango accounts for CPI + 'mangoGroup', + 'mangoCache', + 'mangoRootBank', + 'mangoNodeBank', + 'mangoVault', + // + 'tokenProgram', + 'mangoProgram', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) + const args = dataLayout.decode(Buffer.from(data)) as any console.log('args', args) return ( diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index bc17c7dc22..efed66eef0 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -80,6 +80,11 @@ export default function useGovernanceAssets() { name: 'Deposit Insurance To Mango Depository', isVisible: canUseUxdInstructions, }, + { + id: Instructions.WithdrawInsuranceFromMangoDepository, + name: 'Withdraw Insurance From Mango Depository', + isVisible: canUseUxdInstructions, + }, { id: Instructions.Transfer, name: 'Transfer Tokens', diff --git a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx new file mode 100644 index 0000000000..8995f79e6f --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx @@ -0,0 +1,182 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + WithdrawInsuranceFromMangoDepositoryForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import { Governance, GovernanceAccountType } from '@models/accounts' +import { ParsedAccount } from '@models/core/accounts' +import useWalletStore from 'stores/useWalletStore' +import { serializeInstructionToBase64 } from '@models/serialisation' +import Input from '@components/inputs/Input' +import { debounce } from '@utils/debounce' +import GovernedAccountSelect from '../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import createWithdrawInsuranceFromMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction' + +const WithdrawInsuranceFromMangoDepository = ({ + index, + governance, +}: { + index: number + governance: ParsedAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { getGovernancesByAccountType } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountType( + GovernanceAccountType.ProgramGovernance + ).map((x) => { + return { + governance: x, + } + }) + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({ + governedAccount: undefined, + programId: programId?.toString(), + collateralMint: '', + insuranceMint: '', + insuranceWithdrawnAmount: 0, + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + async function getInstruction(): Promise { + const isValid = await validateInstruction() + let serializedInstruction = '' + if ( + isValid && + programId && + form.governedAccount?.governance?.info && + wallet?.publicKey + ) { + const createIx = await createWithdrawInsuranceFromMangoDepositoryInstruction( + connection.current, + form.governedAccount?.governance.info.governedAccount, + form.governedAccount?.governance.pubkey, + new PublicKey(form.collateralMint), + new PublicKey(form.insuranceMint), + form.insuranceWithdrawnAmount || 0 + ) + serializedInstruction = serializeInstructionToBase64(createIx) + } + const obj: UiInstruction = { + serializedInstruction, + isValid, + governedAccount: form.governedAccount?.governance, + } + return obj + } + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + if (form.collateralMint) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.collateralMint]) + useEffect(() => { + if (form.insuranceMint) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.insuranceMint]) + useEffect(() => { + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) + const schema = yup.object().shape({ + collateralMint: yup + .string() + .required('Collateral Mint address is required'), + insuranceMint: yup.string().required('Insurance Mint address is required'), + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + handleSetForm({ + value: evt.target.value, + propertyName: 'collateralMint', + }) + } + error={formErrors['collateralMint']} + /> + + handleSetForm({ + value: evt.target.value, + propertyName: 'insuranceMint', + }) + } + error={formErrors['insuranceMint']} + /> + + handleSetForm({ + value: evt.target.value, + propertyName: 'insuranceWithdrawnAmount', + }) + } + error={formErrors['global']} + /> + + ) +} + +export default WithdrawInsuranceFromMangoDepository diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index fdb90a2965..668b07c985 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -41,6 +41,7 @@ import SetRedeemGlobalSupplyCap from './components/instructions/SetRedeemGlobalS import RegisterMangoDepository from './components/instructions/RegisterMangoDepository' import SetMangoDepositoriesRedeemableSoftCap from './components/instructions/SetMangoDepositoriesRedeemableSoftCap' import DepositInsuranceToMangoDepository from './components/instructions/DepositInsuranceToMangoDepository' +import WithdrawInsuranceFromMangoDepository from './components/instructions/WithdrawInsuranceFromMangoDepository' const schema = yup.object().shape({ title: yup.string().required('Title is required'), }) @@ -283,6 +284,13 @@ const New = () => { governance={governance} /> ) + case Instructions.WithdrawInsuranceFromMangoDepository: + return ( + + ) case Instructions.Mint: return case Instructions.Base64: diff --git a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts new file mode 100644 index 0000000000..58b96a0c98 --- /dev/null +++ b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts @@ -0,0 +1,37 @@ +import { Provider } from '@project-serum/anchor' +import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { MangoDepository } from '@uxdprotocol/uxd-client' +import { uxdClient, initializeMango } from './uxdClient' + +const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( + connection: Connection, + uxdProgramId: PublicKey, + authority: PublicKey, + depositoryMint: PublicKey, + insuranceMint: PublicKey, + insuranceWithdrawnAmount: number +): Promise => { + const { client, controller } = uxdClient(connection, uxdProgramId) + + const mango = await initializeMango(connection) + const depository = new MangoDepository( + depositoryMint, + 'collateralName', + 6, + insuranceMint, + 'USDC', + 6, + uxdProgramId + ) + + return client.createWithdrawInsuranceFromMangoDepositoryInstruction( + insuranceWithdrawnAmount, + controller, + depository, + mango, + authority, + Provider.defaultOptions() + ) +} + +export default createWithdrawInsuranceFromMangoDepositoryInstruction diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 83c230a2ba..9f8904b8c3 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -63,6 +63,7 @@ export enum Instructions { SetMangoDepositoriesRedeemableSoftCap, RegisterMangoDepository, DepositInsuranceToMangoDepository, + WithdrawInsuranceFromMangoDepository, } export interface InitializeControllerForm { @@ -99,6 +100,14 @@ export interface DepositInsuranceToMangoDepositoryForm { programId: string | undefined } +export interface WithdrawInsuranceFromMangoDepositoryForm { + governedAccount: GovernedProgramAccount | undefined + collateralMint: string + insuranceMint: string + insuranceWithdrawnAmount: number | undefined + programId: string | undefined +} + export enum UXDIntructions { InitializeController, SetRedeemableGlobalSupplyCap, From 4600df4a44bd3abbaa54137439fb207c72d707db Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 12 Dec 2021 10:25:15 +0900 Subject: [PATCH 018/204] update devnet program and urls --- components/RealmHeader.tsx | 5 +- components/instructions/programs/names.ts | 2 +- .../instructions/programs/uxdProtocol.tsx | 179 ++++++++++++++++++ 3 files changed, 183 insertions(+), 3 deletions(-) diff --git a/components/RealmHeader.tsx b/components/RealmHeader.tsx index 382af9c597..415a106e00 100644 --- a/components/RealmHeader.tsx +++ b/components/RealmHeader.tsx @@ -15,9 +15,10 @@ const RealmHeader = () => { const isBackNavVisible = realmInfo?.symbol !== REALM // hide backnav for the default realm const mvpHost = - realmInfo?.symbol === 'MNGO' - ? 'dao.mango.markets' + realmInfo?.symbol === 'UXD' + ? 'governance.uxd.fi' : 'solana-labs.github.io/oyster-gov' + const mvpUrl = `https://${mvpHost}/#/realm/${realmInfo?.realmId.toBase58()}?programId=${realmInfo?.programId.toBase58()}` return ( diff --git a/components/instructions/programs/names.ts b/components/instructions/programs/names.ts index 701a794f74..38a6d5cd07 100644 --- a/components/instructions/programs/names.ts +++ b/components/instructions/programs/names.ts @@ -21,7 +21,7 @@ export const PROGRAM_NAMES = { '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8': 'Raydium AMM Program', EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q: 'Raydium Staking Program', - '73yvDSPxhtXmi8Gaheobm32n3jMdbvQzCahXgqqE12My': 'UXD Protocol Program', + CBSkoqgGtB4772H7FqeE6wz56rLbsxxbVjxEG6JmHzwG: 'UXD Protocol Devnet Program', SysvarRent111111111111111111111111111111111: 'Sysvar: Rent', SysvarC1ock11111111111111111111111111111111: 'Sysvar: Clock', diff --git a/components/instructions/programs/uxdProtocol.tsx b/components/instructions/programs/uxdProtocol.tsx index 14f55786b2..a8e62de1d0 100644 --- a/components/instructions/programs/uxdProtocol.tsx +++ b/components/instructions/programs/uxdProtocol.tsx @@ -143,6 +143,185 @@ export const UXD_PROGRAM_INSTRUCTIONS = { ) }, }, + 6: { + name: 'UXD - Withdraw Insurance From Mango Depository', + accounts: [ + 'authority', + 'controller', + 'depository', + 'insuranceMint', + 'authorityInsurance', + 'depositoryInsurancePassthroughAccount', + 'depositoryMangoAccount', + // mango accounts for CPI + 'mangoGroup', + 'mangoCache', + 'mangoSigner', + 'mangoRootBank', + 'mangoNodeBank', + 'mangoVault', + // + 'tokenProgram', + 'mangoProgram', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) + + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, + }, + CBSkoqgGtB4772H7FqeE6wz56rLbsxxbVjxEG6JmHzwG: { + 1: { + name: 'UXD - Initialize Controller', + accounts: [ + 'authority', + 'payer', + 'controller', + 'redeemableMint', + 'systemProgram', + 'tokenProgram', + 'rent', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([ + u8('bump'), + u8('redeemableBump'), + u8('redeemableMintDecimals'), + ]) + + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, + 2: { + name: 'UXD - Set Redeemable Global Supply Cap', + accounts: ['authority', 'controller'], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) + + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, + 3: { + name: 'UXD - Set Mango Depositories Redeemable Supply Soft Cap', + accounts: ['authority', 'controller'], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) + + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, + 4: { + name: 'UXD - Register Mango Depository', + accounts: [ + 'authority', + 'payer', + 'controller', + 'depository', + 'collateralMint', // BTC/ WSOL..... + 'insuranceMint', // USDC + 'depositoryCollateralPassthroughAccount', + 'depositoryInsurancePassthroughAccount', + 'depositoryMangoAccount', + 'mangoGroup', + 'rent', + 'systemProgram', + 'tokenProgram', + 'mangoProgram', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) + + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, + 5: { + name: 'UXD - Deposit Insurance To Mango Depository', + accounts: [ + 'authority', + 'controller', + 'depository', + 'insuranceMint', + 'authorityInsurance', + 'depositoryInsurancePassthroughAccount', + 'depositoryMangoAccount', + // mango accounts for CPI + 'mangoGroup', + 'mangoCache', + 'mangoSigner', + 'mangoRootBank', + 'mangoNodeBank', + 'mangoVault', + // + 'tokenProgram', + 'mangoProgram', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([u48('redeemable_global_supply_cap')]) + + const args = dataLayout.decode(Buffer.from(data)) as any + console.log('args', args) + return ( + <> +

{args}

+ + ) + }, + }, 6: { name: 'UXD - Withdraw Insurance From Mango Depository', accounts: [ From bd6363c4a29e8119b48aa17ca864982ccca8c3c0 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 12 Dec 2021 16:55:54 +0900 Subject: [PATCH 019/204] fixes from rebase + update URL (Withdraw still not working) --- components/RealmHeader.tsx | 2 +- hooks/useGovernanceAssets.ts | 2 +- public/realms/UXD/favicon.ico | Bin 0 -> 15406 bytes .../realms/{UXDProtocol => UXD}/img/logo.png | Bin public/realms/devnet.json | 9 ++++++++ public/realms/mainnet-beta.json | 4 ++-- stores/useWalletStore.tsx | 6 +++--- ...reateRegisterMangoDepositoryInstruction.ts | 20 +++++++++++++++++- 8 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 public/realms/UXD/favicon.ico rename public/realms/{UXDProtocol => UXD}/img/logo.png (100%) diff --git a/components/RealmHeader.tsx b/components/RealmHeader.tsx index 415a106e00..d2ba007339 100644 --- a/components/RealmHeader.tsx +++ b/components/RealmHeader.tsx @@ -16,7 +16,7 @@ const RealmHeader = () => { const mvpHost = realmInfo?.symbol === 'UXD' - ? 'governance.uxd.fi' + ? 'oyster.uxd.fi' : 'solana-labs.github.io/oyster-gov' const mvpUrl = `https://${mvpHost}/#/realm/${realmInfo?.realmId.toBase58()}?programId=${realmInfo?.programId.toBase58()}` diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index efed66eef0..a13378baf7 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -45,7 +45,7 @@ export default function useGovernanceAssets() { ) const canUseUxdInstructions = - symbol === 'UXD Protocol' && + symbol === 'UXD' && canUseGovernanceForInstruction(GovernanceAccountType.ProgramGovernance) const canUseAnyInstruction = diff --git a/public/realms/UXD/favicon.ico b/public/realms/UXD/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b0807b7c7dc3fd1681f989ba77c59eba612d2c10 GIT binary patch literal 15406 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, get) => ({ ) }) const realmMints = get().selectedRealm.mints - const realmMintPk = realm.info.communityMint - const realmMint = realmMints[realmMintPk.toBase58()] - const realmCouncilMintPk = realm.info.config.councilMint + const realmMintPk = realm?.info.communityMint + const realmMint = realmMints[realmMintPk?.toBase58()] + const realmCouncilMintPk = realm?.info.config.councilMint const realmCouncilMint = realmCouncilMintPk && realmMints[realmCouncilMintPk.toBase58()] const [ diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index 6512ba5a4c..821f7fe99a 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -1,6 +1,9 @@ +import { serializeInstructionToBase64 } from '@models/serialisation' import { Provider } from '@project-serum/anchor' +import { Token, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token' import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' -import { MangoDepository } from '@uxdprotocol/uxd-client' +import { TOKEN_PROGRAM_ID } from '@utils/tokens' +import { findATAAddrSync, MangoDepository } from '@uxdprotocol/uxd-client' import { initializeMango, uxdClient } from './uxdClient' const createRegisterMangoDepositoryInstruction = async ( @@ -23,6 +26,21 @@ const createRegisterMangoDepositoryInstruction = async ( ) const { client, controller } = uxdClient(connection, uxdProgramId) + const [authorityInsuranceATA] = findATAAddrSync(authority, insuranceMint)[0] + const createAuthorityInsuranceItx = Token.createAssociatedTokenAccountInstruction( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + insuranceMint, + findATAAddrSync(authority, insuranceMint)[0], + authority, // owner + payer // payer + ) + + console.log( + `Initialize Authority Insurance ATA (${authorityInsuranceATA.toBase58()}) itx:`, + serializeInstructionToBase64(createAuthorityInsuranceItx) + ) + return client.createRegisterMangoDepositoryInstruction( controller, depository, From 20f85c88528e78352700a4b676494e767dc44fb9 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 12 Dec 2021 16:57:10 +0900 Subject: [PATCH 020/204] lint --- .../sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index 821f7fe99a..9b17fa5132 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -26,7 +26,7 @@ const createRegisterMangoDepositoryInstruction = async ( ) const { client, controller } = uxdClient(connection, uxdProgramId) - const [authorityInsuranceATA] = findATAAddrSync(authority, insuranceMint)[0] + const [authorityInsuranceATA] = findATAAddrSync(authority, insuranceMint) const createAuthorityInsuranceItx = Token.createAssociatedTokenAccountInstruction( ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, From 4dd98feaa4b3c679d37b79aa4548efc6bb5f5469 Mon Sep 17 00:00:00 2001 From: CryptoSheik <93099369+0xCryptoSheik@users.noreply.github.com> Date: Mon, 13 Dec 2021 11:34:49 +0100 Subject: [PATCH 021/204] fix @project-serum/anchor publickey issue --- next.config.js | 7 + package.json | 11 +- patches/@project-serum+anchor+0.19.0.patch | 22 +++ yarn.lock | 195 ++++++++++++--------- 4 files changed, 152 insertions(+), 83 deletions(-) create mode 100644 patches/@project-serum+anchor+0.19.0.patch diff --git a/next.config.js b/next.config.js index cef2575656..16d82789f0 100644 --- a/next.config.js +++ b/next.config.js @@ -5,6 +5,13 @@ const withTM = require('next-transpile-modules')(['react-markdown']) module.exports = withTM({ target: 'serverless', webpack: (config, { isServer }) => { + config.resolve = { + ...config.resolve, + alias: { + ...config.resolve.alias, + '@project-serum/anchor$': '@project-serum/anchor/dist/esm', + }, + } config.module.rules.push({ test: /\.svg$/, use: ['@svgr/webpack'], diff --git a/package.json b/package.json index ed2ea517ff..e339b04bc9 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "lint": "eslint . --ext ts --ext tsx --ext js --ext jsx", "test": "jest", "test-all": "yarn lint && yarn type-check && yarn test", - "notifier": "TS_NODE_PROJECT=./tsconfig.commonjs.json ts-node scripts/governance-notifier.ts" + "notifier": "TS_NODE_PROJECT=./tsconfig.commonjs.json ts-node scripts/governance-notifier.ts", + "postinstall": "patch-package" }, "lint-staged": { "*.@(ts|tsx|js|jsx)": [ @@ -23,16 +24,16 @@ }, "dependencies": { "@blockworks-foundation/mango-client": "^3.2.15", - "@project-serum/anchor": "^0.19.0", - "@solana/spl-token": "^0.1.8", - "@solana/web3.js": "1.31.0", "@emotion/react": "^11.1.5", "@emotion/styled": "^11.3.0", "@headlessui/react": "^1.0.0", "@heroicons/react": "^1.0.1", + "@project-serum/anchor": "^0.19.0", "@project-serum/borsh": "^0.2.2", "@project-serum/common": "^0.0.1-beta.3", "@project-serum/sol-wallet-adapter": "^0.2.0", + "@solana/spl-token": "^0.1.8", + "@solana/web3.js": "1.31.0", "@tippyjs/react": "^4.2.5", "@uxdprotocol/uxd-client": "^2.56.0", "axios": "^0.21.1", @@ -71,8 +72,10 @@ "jest": "^26.6.3", "jest-watch-typeahead": "^0.6.1", "lint-staged": "^10.0.10", + "patch-package": "^6.4.7", "postcss": "^8.2.12", "postcss-preset-env": "^6.7.0", + "postinstall-postinstall": "^2.1.0", "prettier": "^2.0.2", "tailwindcss": "^2.1.2", "twin.macro": "^2.4.0", diff --git a/patches/@project-serum+anchor+0.19.0.patch b/patches/@project-serum+anchor+0.19.0.patch new file mode 100644 index 0000000000..45aa302cd1 --- /dev/null +++ b/patches/@project-serum+anchor+0.19.0.patch @@ -0,0 +1,22 @@ +diff --git a/node_modules/@project-serum/anchor/dist/esm/program/common.js b/node_modules/@project-serum/anchor/dist/esm/program/common.js +index 164d8ff..bab8614 100644 +--- a/node_modules/@project-serum/anchor/dist/esm/program/common.js ++++ b/node_modules/@project-serum/anchor/dist/esm/program/common.js +@@ -38,15 +38,6 @@ export function validateAccounts(ixAccounts, accounts = {}) { + } + // Translates an address to a Pubkey. + export function translateAddress(address) { +- if (typeof address === "string") { +- const pk = new PublicKey(address); +- return pk; +- } +- else if (address.constructor.prototype.constructor.name === "PublicKey") { +- return address; +- } +- else { +- throw new Error("Given address is not a PublicKey nor a string."); +- } ++ return new PublicKey(address); + } + //# sourceMappingURL=common.js.map +\ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 37ebf0f6cb..ad63debd38 100644 --- a/yarn.lock +++ b/yarn.lock @@ -337,7 +337,7 @@ version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" -"@blockworks-foundation/mango-client@^3.2.2": +"@blockworks-foundation/mango-client@^3.2.15": version "3.2.15" resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.2.15.tgz#2b9fdb04068a420af6a8f60c42ca65a53e20a1bc" integrity sha512-oyTMgQ7t6CjUIfJ26RygLg1eSu4zmMypD9iPWAglzFqJkmrwc0HOrr309ylEeUT1P5PxUxl3E3AaxjnhzLjMRw== @@ -353,27 +353,6 @@ dotenv "^10.0.0" yargs "^17.0.1" -"@blockworks-foundation/mango-client@^3.2.7": - version "3.2.14" - resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.2.14.tgz#64854e60d8bf21a571858c0385d8c699af6f639b" - integrity sha512-EBO39zx3wtX4p/BmkEznVOPvuKI8dm33zxDYRvDVLoVGDSnNMBMpRao3bgHiQpGAUp962wX9ScYmLfiZ7qdoEQ== - dependencies: - "@project-serum/anchor" "^0.16.2" - "@project-serum/serum" "0.13.55" - "@project-serum/sol-wallet-adapter" "^0.2.0" - "@solana/spl-token" "^0.1.6" - "@solana/web3.js" "1.21.0" - axios "^0.21.1" - big.js "^6.1.1" - bigint-buffer "^1.1.5" - bn.js "^5.2.0" - buffer-layout "^1.2.1" - cross-fetch "^3.1.4" - dotenv "^10.0.0" - dotenv-expand "^5.1.0" - encoding "^0.1.13" - yargs "^17.0.1" - "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -902,17 +881,17 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/anchor@^0.18.2": - version "0.18.2" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.18.2.tgz#0f13b5c2046446b7c24cf28763eec90febb28485" - integrity sha512-uyjiN/3Ipp+4hrZRm/hG18HzGLZyvP790LXrCsGO3IWxSl28YRhiGEpKnZycfMW94R7nxdUoE3wY67V+ZHSQBQ== +"@project-serum/anchor@^0.19.0": + version "0.19.0" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.19.0.tgz#79f1fbe7c3134860ccbfe458a0e09daf79644885" + integrity sha512-cs0LBmJOrL9eJ8MRNqitnzbpCT5QEzVdJmiIjfNV5YaGn1K9vISR7DtISj3Bdl3KBdLqii4CTw1mpHdi8iXUCg== dependencies: "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" base64-js "^1.5.1" bn.js "^5.1.2" bs58 "^4.0.1" - buffer-layout "^1.2.0" + buffer-layout "^1.2.2" camelcase "^5.3.1" crypto-hash "^1.3.0" eventemitter3 "^4.0.7" @@ -987,17 +966,6 @@ dependencies: buffer "~6.0.3" -"@solana/spl-token@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.3.tgz#6bf7c1a74cd95dabe8b8164e4c13b987db5be3bd" - dependencies: - "@babel/runtime" "^7.10.5" - "@solana/web3.js" "^1.2.2" - bn.js "^5.1.0" - buffer "6.0.3" - buffer-layout "^1.2.0" - dotenv "8.2.0" - "@solana/spl-token@^0.1.6", "@solana/spl-token@^0.1.8": version "0.1.8" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" @@ -1030,10 +998,10 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solana/web3.js@1.30.2", "@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.30.2": - version "1.30.2" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.30.2.tgz#e85da75e0825dc64f53eb64a1ff0115b27bec135" - integrity sha512-hznCj+rkfvM5taRP3Z+l5lumB7IQnDrB4l55Wpsg4kDU9Zds8pE5YOH5Z9bbF/pUzZJKQjyBjnY/6kScBm3Ugg== +"@solana/web3.js@1.31.0": + version "1.31.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.31.0.tgz#7a313d4c1a90b77f27ddbfe845a10d6883e06452" + integrity sha512-7nHHx1JNFnrt15e9y8m38I/EJCbaB+bFC3KZVM1+QhybCikFxGMtGA5r7PDC3GEL1R2RZA8yKoLkDKo3vzzqnw== dependencies: "@babel/runtime" "^7.12.5" "@ethersproject/sha2" "^5.5.0" @@ -1050,25 +1018,7 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solana/web3.js@^1.2.2": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.5.0.tgz#4819ecad0408ec55f3d47a227627856002a7358b" - dependencies: - "@babel/runtime" "^7.12.5" - bn.js "^5.0.0" - bs58 "^4.0.1" - buffer "6.0.1" - buffer-layout "^1.2.0" - crypto-hash "^1.2.2" - jayson "^3.4.4" - js-sha3 "^0.8.0" - node-fetch "^2.6.1" - rpc-websockets "^7.4.2" - secp256k1 "^4.0.2" - superstruct "^0.14.2" - tweetnacl "^1.0.0" - -"@solana/web3.js@^1.30.2": +"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0": version "1.30.2" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.30.2.tgz#e85da75e0825dc64f53eb64a1ff0115b27bec135" integrity sha512-hznCj+rkfvM5taRP3Z+l5lumB7IQnDrB4l55Wpsg4kDU9Zds8pE5YOH5Z9bbF/pUzZJKQjyBjnY/6kScBm3Ugg== @@ -1414,15 +1364,20 @@ "@typescript-eslint/types" "4.33.0" eslint-visitor-keys "^2.0.0" -"@uxdprotocol/uxd-client@^2.46.0": - version "2.46.0" - resolved "https://npm.pkg.github.com/download/@uxdprotocol/uxd-client/2.46.0/8a7720c33a05508924b217d5e9b5645f5c6edbd477dbdaa9543d179aa55a44ff#27da670e96a10c0a23815c24401e8b27b6e5481e" - integrity sha512-lBliolGuPxoKKME1l1COdf0TgWyP5sMyIKrlSNOShcU2I+EdPDHWhvs4e7/cIsRs5+FNMC/XB+Ycxj3yUWfuow== +"@uxdprotocol/uxd-client@^2.56.0": + version "2.56.3" + resolved "https://npm.pkg.github.com/download/@uxdprotocol/uxd-client/2.56.3/3d1efd5c804ba4f2a2b467c6e175a3e0ade7e4f7250504e1abbd76fe87ba57af#da536531ce67c07cf5d1e48842baae264768c87e" + integrity sha512-1+T00nxuqa0AB0bMIQSOVb7eNU6nDKSVSiy3632A63cbe++R0bpfYd1VvlcRkzdm/dkQJxpsQ+MhPFMc1GvsBA== dependencies: - "@blockworks-foundation/mango-client" "^3.2.7" - "@project-serum/anchor" "^0.18.2" + "@blockworks-foundation/mango-client" "^3.2.15" + "@project-serum/anchor" "^0.19.0" "@solana/spl-token" "^0.1.8" - "@solana/web3.js" "1.30.2" + "@solana/web3.js" "1.31.0" + +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== JSONStream@^1.3.5: version "1.3.5" @@ -1984,7 +1939,7 @@ buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" -buffer-layout@^1.2.0, buffer-layout@^1.2.1: +buffer-layout@^1.2.0, buffer-layout@^1.2.1, buffer-layout@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5" integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA== @@ -2407,7 +2362,7 @@ cross-fetch@^3.1.4: dependencies: node-fetch "2.6.1" -cross-spawn@^6.0.0: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" dependencies: @@ -2729,10 +2684,6 @@ dotenv@10.0.0, dotenv@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -dotenv@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" - dset@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/dset/-/dset-2.1.0.tgz#cd1e99e55cf32366d8f144f906c42f7fb3bf431e" @@ -2780,7 +2731,7 @@ emojis-list@^2.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= -encoding@0.1.13, encoding@^0.1.13: +encoding@0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -3252,6 +3203,13 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + find@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/find/-/find-0.3.0.tgz#4082e8fc8d8320f1a382b5e4f521b9bc50775cb8" @@ -3310,6 +3268,15 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -3461,7 +3428,7 @@ globby@^11.0.3: merge2 "^1.3.0" slash "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.2.4: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== @@ -4049,7 +4016,7 @@ is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" -is-wsl@^2.2.0: +is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" dependencies: @@ -4606,6 +4573,13 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -4655,6 +4629,13 @@ kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -5504,6 +5485,14 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +open@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -5531,6 +5520,11 @@ os-browserify@0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + p-each-series@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" @@ -5636,6 +5630,25 @@ pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" +patch-package@^6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148" + integrity sha512-S0vh/ZEafZ17hbhgqdnpunKDfzHQibQizx9g8yEf5dcVk3KOflOfdufRXQX8CSEkyOQwuM/bNz1GwKvFj54kaQ== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^2.4.2" + cross-spawn "^6.0.5" + find-yarn-workspace-root "^2.0.0" + fs-extra "^7.0.1" + is-ci "^2.0.0" + klaw-sync "^6.0.0" + minimist "^1.2.0" + open "^7.4.2" + rimraf "^2.6.3" + semver "^5.6.0" + slash "^2.0.0" + tmp "^0.0.33" + path-browserify@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" @@ -6071,6 +6084,11 @@ postcss@^8.2.12: picocolors "^1.0.0" source-map-js "^1.0.1" +postinstall-postinstall@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" + integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -6527,6 +6545,13 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -6632,7 +6657,7 @@ semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" -"semver@2 || 3 || 4 || 5", semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -6733,6 +6758,11 @@ sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -7232,6 +7262,13 @@ tippy.js@^6.3.1: dependencies: "@popperjs/core" "^2.8.3" +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" @@ -7543,7 +7580,7 @@ unist-util-visit@^4.0.0: unist-util-is "^5.0.0" unist-util-visit-parents "^5.0.0" -universalify@^0.1.2: +universalify@^0.1.0, universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" From 7e836794494fef95bf073ba810cc5d125ebdbd08 Mon Sep 17 00:00:00 2001 From: Acammm Date: Tue, 14 Dec 2021 10:38:38 +0100 Subject: [PATCH 022/204] Update realm info --- public/realms/mainnet-beta.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/realms/mainnet-beta.json b/public/realms/mainnet-beta.json index ac54e4ac2c..5a0adb61c4 100644 --- a/public/realms/mainnet-beta.json +++ b/public/realms/mainnet-beta.json @@ -139,8 +139,8 @@ { "symbol": "UXD", "displayName": "UXD Protocol DAO", - "programId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", - "realmId": "DkSvNgykZPPFczhJVh8HDkhz25ByrDoPcB32q75AYu9k", + "programId": "UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa", + "realmId": "UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M", "ogImage": "/realms/UXD/img/logo.png", "website": "https://www.uxd.fi.com/", "twitter": "@UXDProtocol" From eb47811db1b48485c15b3d4cb105359c04ba5b76 Mon Sep 17 00:00:00 2001 From: Acammm Date: Tue, 14 Dec 2021 10:43:03 +0100 Subject: [PATCH 023/204] fix realm id --- public/realms/mainnet-beta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/realms/mainnet-beta.json b/public/realms/mainnet-beta.json index 5a0adb61c4..902db9ce77 100644 --- a/public/realms/mainnet-beta.json +++ b/public/realms/mainnet-beta.json @@ -140,7 +140,7 @@ "symbol": "UXD", "displayName": "UXD Protocol DAO", "programId": "UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa", - "realmId": "UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M", + "realmId": "GGt3TagE3111gd7ChWMczWDsCnkEriXyjRPzQDejJbNK", "ogImage": "/realms/UXD/img/logo.png", "website": "https://www.uxd.fi.com/", "twitter": "@UXDProtocol" From 694e6817796021b9d8f1bb70f4d2895ace79b289 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Wed, 15 Dec 2021 16:23:35 +0900 Subject: [PATCH 024/204] yarn lock --- yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index c48b8222bd..39d2216a1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -974,7 +974,7 @@ dependencies: buffer "~6.0.3" -"@solana/spl-token@^0.1.6", "@solana/spl-token@^0.1.8": +"@solana/spl-token@^0.1.3", "@solana/spl-token@^0.1.6", "@solana/spl-token@^0.1.8": version "0.1.8" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" integrity sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ== @@ -1025,7 +1025,7 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solana/web3.js@1.31.0": +"@solana/web3.js@1.31.0", "@solana/web3.js@^1.30.2": version "1.31.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.31.0.tgz#7a313d4c1a90b77f27ddbfe845a10d6883e06452" integrity sha512-7nHHx1JNFnrt15e9y8m38I/EJCbaB+bFC3KZVM1+QhybCikFxGMtGA5r7PDC3GEL1R2RZA8yKoLkDKo3vzzqnw== From d8bbb71809c269739f9e70259277653a5c8b1436 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Wed, 15 Dec 2021 16:30:51 +0900 Subject: [PATCH 025/204] update UiInstruction obj --- .../instructions/DepositInsuranceToMangoDepository.tsx | 2 +- .../proposal/components/instructions/InitializeController.tsx | 2 +- .../components/instructions/RegisterMangoDepository.tsx | 2 +- .../instructions/SetMangoDepositoriesRedeemableSoftCap.tsx | 2 +- .../components/instructions/SetRedeemGlobalSupplyCap.tsx | 2 +- .../instructions/WithdrawInsuranceFromMangoDepository.tsx | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx index d2df913665..64531e7f16 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx @@ -80,7 +80,7 @@ const DepositInsuranceToMangoDepository = ({ const obj: UiInstruction = { serializedInstruction, isValid, - governedAccount: form.governedAccount?.governance, + governance: form.governedAccount?.governance, } return obj } diff --git a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx index 9034fcaa86..1a152e988f 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx @@ -79,7 +79,7 @@ const InitializeController = ({ const obj: UiInstruction = { serializedInstruction, isValid, - governedAccount: form.governedAccount?.governance, + governance: form.governedAccount?.governance, } return obj } diff --git a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx index c375d5857b..4ecfa09213 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx @@ -79,7 +79,7 @@ const RegisterMangoDepository = ({ const obj: UiInstruction = { serializedInstruction, isValid, - governedAccount: form.governedAccount?.governance, + governance: form.governedAccount?.governance, } return obj } diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx index cf69ca53d9..fb6c698f86 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -76,7 +76,7 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ const obj: UiInstruction = { serializedInstruction, isValid, - governedAccount: form.governedAccount?.governance, + governance: form.governedAccount?.governance, } return obj } diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx index ee5b689666..c3074163ff 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx @@ -76,7 +76,7 @@ const SetRedeemGlobalSupplyCap = ({ const obj: UiInstruction = { serializedInstruction, isValid, - governedAccount: form.governedAccount?.governance, + governance: form.governedAccount?.governance, } return obj } diff --git a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx index 8995f79e6f..30528540d2 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx @@ -80,7 +80,7 @@ const WithdrawInsuranceFromMangoDepository = ({ const obj: UiInstruction = { serializedInstruction, isValid, - governedAccount: form.governedAccount?.governance, + governance: form.governedAccount?.governance, } return obj } From 6c53edef76460a77135f9151e2ab8d3a0cb68100 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 19 Dec 2021 00:16:23 +0900 Subject: [PATCH 026/204] comment out github workflow for now --- .github/workflows/pull-request.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index ab46c7107f..7cc23c1f6d 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -1,16 +1,16 @@ -on: - pull_request: - types: [opened, synchronize] +# on: +# pull_request: +# types: [opened, synchronize] -jobs: - test: - name: yarn test-all - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: lts/* - cache: yarn - - run: yarn install - - run: yarn test-all +# jobs: +# test: +# name: yarn test-all +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v2 +# - uses: actions/setup-node@v2 +# with: +# node-version: lts/* +# cache: yarn +# - run: yarn install +# - run: yarn test-all From a5a4861f9e173d2322176f1dbe05b735dce2bfa8 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 19 Dec 2021 00:19:30 +0900 Subject: [PATCH 027/204] re update @uxdprotocol/uxd-client to latest version --- .github/workflows/pull-request.yml | 30 +++++++++++++-------------- package.json | 2 +- yarn.lock | 33 ++++++++++++++---------------- 3 files changed, 31 insertions(+), 34 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 7cc23c1f6d..ab46c7107f 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -1,16 +1,16 @@ -# on: -# pull_request: -# types: [opened, synchronize] +on: + pull_request: + types: [opened, synchronize] -# jobs: -# test: -# name: yarn test-all -# runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v2 -# - uses: actions/setup-node@v2 -# with: -# node-version: lts/* -# cache: yarn -# - run: yarn install -# - run: yarn test-all +jobs: + test: + name: yarn test-all + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: lts/* + cache: yarn + - run: yarn install + - run: yarn test-all diff --git a/package.json b/package.json index ddcc45d1df..ab631b9737 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@solana/wallet-adapter-sollet": "^0.8.0", "@solana/web3.js": "^1.30.2", "@tippyjs/react": "^4.2.5", - "@uxdprotocol/uxd-client": "^2.56.0", + "@uxdprotocol/uxd-client": "^3.1.0", "axios": "^0.21.1", "bignumber.js": "^9.0.1", "immer": "^9.0.1", diff --git a/yarn.lock b/yarn.lock index f0cb78ce92..ca25273556 100644 --- a/yarn.lock +++ b/yarn.lock @@ -337,7 +337,7 @@ version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" -"@blockworks-foundation/mango-client@^3.2.15": +"@blockworks-foundation/mango-client@3.2.15", "@blockworks-foundation/mango-client@^3.2.15": version "3.2.15" resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.2.15.tgz#2b9fdb04068a420af6a8f60c42ca65a53e20a1bc" integrity sha512-oyTMgQ7t6CjUIfJ26RygLg1eSu4zmMypD9iPWAglzFqJkmrwc0HOrr309ylEeUT1P5PxUxl3E3AaxjnhzLjMRw== @@ -989,7 +989,7 @@ dependencies: buffer "~6.0.3" -"@solana/spl-token@^0.1.3", "@solana/spl-token@^0.1.6", "@solana/spl-token@^0.1.8": +"@solana/spl-token@^0.1.3", "@solana/spl-token@^0.1.6": version "0.1.8" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" integrity sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ== @@ -1040,10 +1040,10 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solana/web3.js@1.31.0", "@solana/web3.js@^1.30.2": - version "1.31.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.31.0.tgz#7a313d4c1a90b77f27ddbfe845a10d6883e06452" - integrity sha512-7nHHx1JNFnrt15e9y8m38I/EJCbaB+bFC3KZVM1+QhybCikFxGMtGA5r7PDC3GEL1R2RZA8yKoLkDKo3vzzqnw== +"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0": + version "1.30.2" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.30.2.tgz#e85da75e0825dc64f53eb64a1ff0115b27bec135" + integrity sha512-hznCj+rkfvM5taRP3Z+l5lumB7IQnDrB4l55Wpsg4kDU9Zds8pE5YOH5Z9bbF/pUzZJKQjyBjnY/6kScBm3Ugg== dependencies: "@babel/runtime" "^7.12.5" "@ethersproject/sha2" "^5.5.0" @@ -1060,10 +1060,10 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0": - version "1.30.2" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.30.2.tgz#e85da75e0825dc64f53eb64a1ff0115b27bec135" - integrity sha512-hznCj+rkfvM5taRP3Z+l5lumB7IQnDrB4l55Wpsg4kDU9Zds8pE5YOH5Z9bbF/pUzZJKQjyBjnY/6kScBm3Ugg== +"@solana/web3.js@^1.30.2": + version "1.31.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.31.0.tgz#7a313d4c1a90b77f27ddbfe845a10d6883e06452" + integrity sha512-7nHHx1JNFnrt15e9y8m38I/EJCbaB+bFC3KZVM1+QhybCikFxGMtGA5r7PDC3GEL1R2RZA8yKoLkDKo3vzzqnw== dependencies: "@babel/runtime" "^7.12.5" "@ethersproject/sha2" "^5.5.0" @@ -1411,15 +1411,12 @@ "@typescript-eslint/types" "5.7.0" eslint-visitor-keys "^3.0.0" -"@uxdprotocol/uxd-client@^2.56.0": - version "2.56.3" - resolved "https://npm.pkg.github.com/download/@uxdprotocol/uxd-client/2.56.3/3d1efd5c804ba4f2a2b467c6e175a3e0ade7e4f7250504e1abbd76fe87ba57af#da536531ce67c07cf5d1e48842baae264768c87e" - integrity sha512-1+T00nxuqa0AB0bMIQSOVb7eNU6nDKSVSiy3632A63cbe++R0bpfYd1VvlcRkzdm/dkQJxpsQ+MhPFMc1GvsBA== +"@uxdprotocol/uxd-client@^3.1.0": + version "3.2.0" + resolved "https://npm.pkg.github.com/download/@uxdprotocol/uxd-client/3.2.0/8588d6be3ef0a2e17f2b740dfcf31e6e0a0628da6af067d1c63d75cc19c51c2f#2199bf5eb4f7f0c1f93f772b392465c5d8bf5c92" + integrity sha512-I/iVOKhp1BXOvIyal4XHiJBE5klLtIDPj56N0pHd2RJ7RB6jZnkTLqvfBEpUdIePsIzlhyJ+lPRShytXhGoxjg== dependencies: - "@blockworks-foundation/mango-client" "^3.2.15" - "@project-serum/anchor" "^0.19.0" - "@solana/spl-token" "^0.1.8" - "@solana/web3.js" "1.31.0" + "@blockworks-foundation/mango-client" "3.2.15" "@yarnpkg/lockfile@^1.1.0": version "1.1.0" From 0a1bef24b2c85832f4959b741d44aff2c5f1ccbf Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 19 Dec 2021 00:32:17 +0900 Subject: [PATCH 028/204] remove workflow --- .github/workflows/pull-request.yml | 16 ---------------- .gitignore | 1 + 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 .github/workflows/pull-request.yml diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml deleted file mode 100644 index ab46c7107f..0000000000 --- a/.github/workflows/pull-request.yml +++ /dev/null @@ -1,16 +0,0 @@ -on: - pull_request: - types: [opened, synchronize] - -jobs: - test: - name: yarn test-all - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: lts/* - cache: yarn - - run: yarn install - - run: yarn test-all diff --git a/.gitignore b/.gitignore index e9dde829b5..5d520a7b7d 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,5 @@ yarn-error.log* .vercel +/.github/ .npmrc \ No newline at end of file From b3fc337c8660e61b43146276fb13f19cce75bed3 Mon Sep 17 00:00:00 2001 From: Acammm Date: Wed, 15 Dec 2021 09:55:08 +0100 Subject: [PATCH 029/204] Update program to good one, remove mint symbol --- components/instructions/programs/names.ts | 2 +- .../instructions/programs/uxdProtocol.tsx | 2 +- .../instructions/InitializeController.tsx | 32 ++----------------- .../createInitializeControllerInstruction.ts | 3 +- utils/uiTypes/proposalCreationTypes.ts | 1 - 5 files changed, 5 insertions(+), 35 deletions(-) diff --git a/components/instructions/programs/names.ts b/components/instructions/programs/names.ts index 38a6d5cd07..650ab3bdbb 100644 --- a/components/instructions/programs/names.ts +++ b/components/instructions/programs/names.ts @@ -21,7 +21,7 @@ export const PROGRAM_NAMES = { '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8': 'Raydium AMM Program', EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q: 'Raydium Staking Program', - CBSkoqgGtB4772H7FqeE6wz56rLbsxxbVjxEG6JmHzwG: 'UXD Protocol Devnet Program', + UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa: 'UXD Protocol Program', SysvarRent111111111111111111111111111111111: 'Sysvar: Rent', SysvarC1ock11111111111111111111111111111111: 'Sysvar: Clock', diff --git a/components/instructions/programs/uxdProtocol.tsx b/components/instructions/programs/uxdProtocol.tsx index a8e62de1d0..3f618b446b 100644 --- a/components/instructions/programs/uxdProtocol.tsx +++ b/components/instructions/programs/uxdProtocol.tsx @@ -3,7 +3,7 @@ import { struct, u8, u48 } from 'buffer-layout' import { AccountMetaData } from '../../../models/accounts' export const UXD_PROGRAM_INSTRUCTIONS = { - '73yvDSPxhtXmi8Gaheobm32n3jMdbvQzCahXgqqE12My': { + UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa: { 1: { name: 'UXD - Initialize Controller', accounts: [ diff --git a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx index 1a152e988f..512061ea28 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx @@ -43,7 +43,6 @@ const InitializeController = ({ const [form, setForm] = useState({ governedAccount: undefined, programId: programId?.toString(), - mintSymbol: '', mintDecimals: 0, }) const [formErrors, setFormErrors] = useState({}) @@ -64,12 +63,12 @@ const InitializeController = ({ isValid && programId && form.governedAccount?.governance?.info && + form.mintDecimals && wallet?.publicKey ) { const createIx = createInitializeControllerInstruction( form.governedAccount?.governance.info.governedAccount, - form.mintSymbol || '', - form.mintDecimals || 9, + form.mintDecimals, form.governedAccount?.governance.pubkey, new PublicKey(wallet.publicKey.toBase58()), connection.current @@ -90,22 +89,7 @@ const InitializeController = ({ }) }, [realmInfo?.programId]) - useEffect(() => { - if (form.mintSymbol) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.mintSymbol]) - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) const schema = yup.object().shape({ - mintSymbol: yup.string().required('Mint Symbol is required'), mintDecimals: yup.number().required('Mint Decimals is required'), governedAccount: yup .object() @@ -126,18 +110,6 @@ const InitializeController = ({ shouldBeGoverned={shouldBeGoverned} governance={governance} > - - handleSetForm({ - value: evt.target.value, - propertyName: 'mintSymbol', - }) - } - error={formErrors['mintSymbol']} - /> Date: Wed, 15 Dec 2021 10:03:35 +0100 Subject: [PATCH 030/204] Fix names --- components/instructions/programs/names.ts | 3 ++- .../proposal/components/instructions/InitializeController.tsx | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/instructions/programs/names.ts b/components/instructions/programs/names.ts index 650ab3bdbb..627050c66f 100644 --- a/components/instructions/programs/names.ts +++ b/components/instructions/programs/names.ts @@ -7,6 +7,7 @@ export const GOVERNANCE_PROGRAM_NAMES = { '5sGZEdn32y8nHax7TxEyoHuPS3UXfPWtisgm8kqxat8H': 'Phantasia Governance', smfjietFKFJ4Sbw1cqESBTpPhF4CwbMwN8kBEC1e5ui: 'Strangemood Foundation Governance', + UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa: 'UXD Protocol Governance', } // Well known program names displayed on the instruction card @@ -21,7 +22,7 @@ export const PROGRAM_NAMES = { '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8': 'Raydium AMM Program', EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q: 'Raydium Staking Program', - UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa: 'UXD Protocol Program', + UXDQDbkAeGMPR7gqDykDNu22D9DnYrKdvZhvNmMu6QX: 'UXDProtocol Program', SysvarRent111111111111111111111111111111111: 'Sysvar: Rent', SysvarC1ock11111111111111111111111111111111: 'Sysvar: Clock', diff --git a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx index 512061ea28..690bd1c7d9 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx @@ -15,7 +15,6 @@ import { ParsedAccount } from '@models/core/accounts' import useWalletStore from 'stores/useWalletStore' import { serializeInstructionToBase64 } from '@models/serialisation' import Input from '@components/inputs/Input' -import { debounce } from '@utils/debounce' import GovernedAccountSelect from '../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createInitializeControllerInstruction from '@tools/sdk/uxdProtocol/createInitializeControllerInstruction' From edb751e33a7eb546364c35ec3604ea75287a1da5 Mon Sep 17 00:00:00 2001 From: Acammm Date: Wed, 15 Dec 2021 10:08:19 +0100 Subject: [PATCH 031/204] Fix mistake removal --- .../components/instructions/InitializeController.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx index 690bd1c7d9..4daf3b945b 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx @@ -88,6 +88,12 @@ const InitializeController = ({ }) }, [realmInfo?.programId]) + useEffect(() => { + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) const schema = yup.object().shape({ mintDecimals: yup.number().required('Mint Decimals is required'), governedAccount: yup From 2108e8f4db962895aee1b91fcf02ab5e8c33300d Mon Sep 17 00:00:00 2001 From: Acammm Date: Wed, 15 Dec 2021 10:11:32 +0100 Subject: [PATCH 032/204] Update IDL to latest --- tools/sdk/uxdProtocol/uxdIdl.ts | 108 ++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 40 deletions(-) diff --git a/tools/sdk/uxdProtocol/uxdIdl.ts b/tools/sdk/uxdProtocol/uxdIdl.ts index 108db6df86..c746d21748 100644 --- a/tools/sdk/uxdProtocol/uxdIdl.ts +++ b/tools/sdk/uxdProtocol/uxdIdl.ts @@ -1,7 +1,7 @@ import { Idl } from '@project-serum/anchor' const uxdIdl: Idl = { - version: '0.0.0', + version: '1.2.0', name: 'uxd', instructions: [ { @@ -352,6 +352,11 @@ const uxdIdl: Idl = { isMut: true, isSigner: false, }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, { name: 'tokenProgram', isMut: false, @@ -375,7 +380,7 @@ const uxdIdl: Idl = { accounts: [ { name: 'user', - isMut: false, + isMut: true, isSigner: true, }, { @@ -500,7 +505,7 @@ const uxdIdl: Idl = { accounts: [ { name: 'user', - isMut: false, + isMut: true, isSigner: true, }, { @@ -742,6 +747,14 @@ const uxdIdl: Idl = { name: 'redeemableAmountUnderManagement', type: 'u128', }, + { + name: 'deltaNeutralQuoteFeeOffset', + type: 'u128', + }, + { + name: 'deltaNeutralQuotePosition', + type: 'u128', + }, ], }, }, @@ -764,207 +777,222 @@ const uxdIdl: Idl = { ], errors: [ { - code: 300, + code: 6000, name: 'InvalidRedeemableMintDecimals', msg: 'The redeemable mint decimals must be between 0 and 9 (inclusive).', }, { - code: 301, + code: 6001, name: 'InvalidRedeemableGlobalSupplyCap', msg: 'The redeemable global supply cap must be below MAX_REDEEMABLE_GLOBAL_SUPPLY_CAP.', }, { - code: 302, + code: 6002, name: 'RootBankIndexNotFound', msg: 'The associated mango root bank index cannot be found for the deposited coin..', }, { - code: 303, + code: 6003, name: 'InvalidSlippage', msg: 'The slippage value is invalid. Must be in the [0...1000] range points.', }, { - code: 304, + code: 6004, name: 'InvalidCollateralMint', msg: "The provided collateral mint does not match the depository's collateral mint.", }, { - code: 305, + code: 6005, name: 'InvalidInsuranceMint', msg: "The provided insurance mint does not match the depository's insurance mint.", }, { - code: 306, + code: 6006, name: 'InvalidCollateralAmount', msg: 'Collateral amount must be > 0 in order to mint.', }, { - code: 307, + code: 6007, name: 'InsuficientCollateralAmount', msg: 'The balance of the collateral ATA is not enough to fulfill the mint operation.', }, { - code: 308, + code: 6008, name: 'InvalidRedeemableAmount', msg: 'The redeemable amount for redeem must be superior to 0.', }, { - code: 309, + code: 6009, name: 'InsuficientRedeemableAmount', msg: 'The balance of the redeemable ATA is not enough to fulfill the redeem operation.', }, { - code: 310, + code: 6010, name: 'InvalidAuthority', msg: 'Only the Program initializer authority can access this instructions.', }, { - code: 311, + code: 6011, name: 'InvalidRedeemableMint', msg: "The Redeemable Mint provided does not match the Controller's one.", }, { - code: 312, + code: 6012, name: 'InvalidUserRedeemableATAMint', msg: "The user's Redeemable ATA's mint does not match the Controller's one.", }, { - code: 313, + code: 6013, name: 'InvalidUserCollateralATAMint', msg: "The user's Collateral ATA's mint does not match the Depository's one.", }, { - code: 314, + code: 6014, name: 'InvalidAuthorityInsuranceATAMint', msg: "The authority's Insurance ATA's mint does not match the Depository's one.", }, { - code: 315, + code: 6015, name: 'InvalidCollateralPassthroughATAMint', msg: "The Depository Collateral Passthrough ATA's mint does not match the Depository's one.", }, { - code: 316, + code: 6016, name: 'InvalidInsurancePassthroughATAMint', msg: "The Depository Insurance Passthrough ATA's mint does not match the Depository's one.", }, { - code: 317, + code: 6017, name: 'PerpOrderPartiallyFilled', msg: 'The perp position could not be fully filled with the provided slippage.', }, { - code: 318, + code: 6018, name: 'PositionAmountCalculation', msg: 'Error while getting the redeemable value of the deposited coin amount.', }, { - code: 319, + code: 6019, name: 'RedeemableGlobalSupplyCapReached', msg: 'Minting amount would go past the Redeemable Global Supply Cap.', }, { - code: 320, + code: 6020, name: 'MangoDepositoriesSoftCapOverflow', msg: 'Operation not allowed due to being over the Redeemable soft Cap.', }, { - code: 330, + code: 6030, name: 'MaxNumberOfMangoDepositoriesRegisteredReached', msg: 'Cannot register more mango depositories, the limit has been reached.', }, { - code: 331, + code: 6031, name: 'InvalidController', msg: "The Depository's controller doesn't match the provided Controller.", }, { - code: 332, + code: 6032, name: 'InvalidDepository', msg: 'The Depository provided is not registered with the Controller.', }, { - code: 333, + code: 6033, name: 'InvalidCollateralPassthroughAccount', msg: "The Collateral Passthrough Account isn't the Deposiroty one.", }, { - code: 334, + code: 6034, name: 'InvalidInsurancePassthroughAccount', msg: "The Insurance Passthrough Account isn't the Deposiroty one.", }, { - code: 335, + code: 6035, name: 'InvalidMangoAccount', msg: "The Mango Account isn't the Deposiroty one.", }, { - code: 336, + code: 6036, name: 'InvalidInsuranceAmount', msg: 'The amount to withdraw from the Insurance Fund must be superior to zero..', }, { - code: 337, + code: 6037, name: 'InsuficientAuthorityInsuranceAmount', msg: "The Insurance ATA from authority doesn't have enough balance.", }, { - code: 338, + code: 6038, + name: 'InvalidRebalancingAmount', + msg: 'The max amount to rebalance must be superior to zero..', + }, + { + code: 6039, name: 'InsuficentOrderBookDepth', msg: 'Insuficcent order book depth for order.', }, { - code: 339, + code: 6040, name: 'InvalidExecutedOrderSize', msg: 'The executed order size does not match the expected one.', }, { - code: 380, + code: 6080, name: 'MangoOrderBookLoading', msg: 'Could not load Mango Order book.', }, { - code: 381, + code: 6081, name: 'MangoGroupLoading', msg: 'Could not load Mango Group.', }, { - code: 382, + code: 6082, name: 'MangoCacheLoading', msg: 'Could not load Mango Cache.', }, { - code: 383, + code: 6083, name: 'MangoLoadPerpMarket', msg: 'Could not load Mango PerpMarket.', }, { - code: 384, + code: 6084, name: 'MangoAccountLoading', msg: 'Could not load Mango Account.', }, { - code: 385, + code: 6085, name: 'MangoPerpMarketIndexNotFound', msg: 'Could not find the perp market index for the given collateral.', }, + { + code: 6086, + name: 'InvalidPerpAccountState', + msg: 'The Mango PerpAccount has uncommitted changes.', + }, + { + code: 6087, + name: 'InvalidDepositoryAccounting', + msg: 'The Depository accounting is in an invalid state.', + }, ], } From f905aa36c1bc6c0d2ff86efe108a1b3e5e811105 Mon Sep 17 00:00:00 2001 From: Acammm Date: Wed, 15 Dec 2021 10:17:14 +0100 Subject: [PATCH 033/204] Actually use the mint decimals. Danger --- .../components/instructions/InitializeController.tsx | 6 ++++-- .../uxdProtocol/createInitializeControllerInstruction.ts | 8 ++++++-- tools/sdk/uxdProtocol/uxdClient.ts | 3 ++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx index 4daf3b945b..2a0d9c0b60 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx @@ -65,14 +65,16 @@ const InitializeController = ({ form.mintDecimals && wallet?.publicKey ) { - const createIx = createInitializeControllerInstruction( + const initializeControllerIx = createInitializeControllerInstruction( form.governedAccount?.governance.info.governedAccount, form.mintDecimals, form.governedAccount?.governance.pubkey, new PublicKey(wallet.publicKey.toBase58()), connection.current ) - serializedInstruction = serializeInstructionToBase64(createIx) + serializedInstruction = serializeInstructionToBase64( + initializeControllerIx + ) } const obj: UiInstruction = { serializedInstruction, diff --git a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts index 3448558a07..c73289d056 100644 --- a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts +++ b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts @@ -4,12 +4,16 @@ import { uxdClient } from './uxdClient' const createInitializeControllerInstruction = ( uxdProgramId: PublicKey, - _mintDecimals: number, + mintDecimals: number, authority: PublicKey, payer: PublicKey, connection: Connection ): TransactionInstruction => { - const { client, controller } = uxdClient(connection, uxdProgramId) + const { client, controller } = uxdClient( + mintDecimals, + connection, + uxdProgramId + ) return client.createInitializeControllerInstruction( controller, diff --git a/tools/sdk/uxdProtocol/uxdClient.ts b/tools/sdk/uxdProtocol/uxdClient.ts index 96125c9229..8825e0d029 100644 --- a/tools/sdk/uxdProtocol/uxdClient.ts +++ b/tools/sdk/uxdProtocol/uxdClient.ts @@ -12,6 +12,7 @@ const DEFAULT_WALLET_PROVIDER = 'https://sollet.io' const CLUSTER = 'devnet' export const uxdClient = ( + mintDecimals: number, connection: Connection, programId: PublicKey ): { client: UXD; controller: Controller } => { @@ -19,7 +20,7 @@ export const uxdClient = ( const provider = new Provider(connection, wallet, Provider.defaultOptions()) return { client: new UXD(new Program(uxdIdl, programId, provider)), - controller: new Controller('UXD', 6, programId), + controller: new Controller('redeemable_ticker', mintDecimals, programId), // The redeemable ticker is just local, doesn't matter. } } From e98718ce8dc3a7357204dc3498294392da652ba6 Mon Sep 17 00:00:00 2001 From: Acammm Date: Wed, 15 Dec 2021 11:01:04 +0100 Subject: [PATCH 034/204] Add comments + program ID --- tools/sdk/uxdProtocol/uxdClient.ts | 31 ++++++++++++++++++++++++++++++ tools/sdk/uxdProtocol/uxdIdl.ts | 2 ++ 2 files changed, 33 insertions(+) diff --git a/tools/sdk/uxdProtocol/uxdClient.ts b/tools/sdk/uxdProtocol/uxdClient.ts index 8825e0d029..f7699b7a39 100644 --- a/tools/sdk/uxdProtocol/uxdClient.ts +++ b/tools/sdk/uxdProtocol/uxdClient.ts @@ -29,3 +29,34 @@ export const initializeMango = async (connection: Connection) => { const provider = new Provider(connection, wallet, Provider.defaultOptions()) return createAndInitializeMango(provider, CLUSTER) } + +// import { Program, Provider } from '@project-serum/anchor' +// import Wallet from '@project-serum/sol-wallet-adapter' +// import { Connection } from '@solana/web3.js' +// import { +// createAndInitializeMango, +// UXD, +// UXDHelpers, +// } from '@uxdprotocol/uxd-client' +// import useWalletStore from 'stores/useWalletStore' +// import uxdIdl, { UXD_PROGRAM_ID } from './uxdIdl' + +// export const uxdHelpers = new UXDHelpers(); + +// export const uxdClient = (): UXD => { +// const connection = useWalletStore((s) => s.connection) +// const wallet = useWalletStore((s) => s.current) + +// // PROBABLY NOT OK to fix +// const provider = new Provider(connection.current, wallet, Provider.defaultOptions()); + +// const uxdProgram = new Program(uxdIdl, UXD_PROGRAM_ID, provider); +// return new UXD(new Program(uxdIdl, UXD_PROGRAM_ID, provider)); +// } + +// export const initializeMango = async () => { +// const connection = useWalletStore((s) => s.connection) +// const wallet = useWalletStore((s) => s.current) +// const provider = new Provider(connection.current, wallet, Provider.defaultOptions()) +// return createAndInitializeMango(provider, `mainnet`); +// } diff --git a/tools/sdk/uxdProtocol/uxdIdl.ts b/tools/sdk/uxdProtocol/uxdIdl.ts index c746d21748..7dc8ee7247 100644 --- a/tools/sdk/uxdProtocol/uxdIdl.ts +++ b/tools/sdk/uxdProtocol/uxdIdl.ts @@ -1,5 +1,7 @@ import { Idl } from '@project-serum/anchor' +export const UXD_PROGRAM_ID = 'UXDQDbkAeGMPR7gqDykDNu22D9DnYrKdvZhvNmMu6QX' + const uxdIdl: Idl = { version: '1.2.0', name: 'uxd', From 74066155e09c1c619d93142885072ed157ea55ba Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Wed, 15 Dec 2021 23:58:11 +0900 Subject: [PATCH 035/204] get controller pda from input and cluster/connection dynamically --- .../DepositInsuranceToMangoDepository.tsx | 18 ++++++++-- .../instructions/RegisterMangoDepository.tsx | 34 +++++++++++++++++-- .../SetMangoDepositoriesRedeemableSoftCap.tsx | 17 +++++++++- .../instructions/SetRedeemGlobalSupplyCap.tsx | 17 +++++++++- .../WithdrawInsuranceFromMangoDepository.tsx | 18 ++++++++-- ...itInsuranceToMangoDepositoryInstruction.ts | 16 +++++---- .../createInitializeControllerInstruction.ts | 7 ++-- ...reateRegisterMangoDepositoryInstruction.ts | 20 +++++++---- ...epositoriesRedeemableSoftCapInstruction.ts | 8 +++-- ...SetRedeemableGlobalSupplyCapInstruction.ts | 8 +++-- ...InsuranceFromMangoDepositoryInstruction.ts | 16 +++++---- tools/sdk/uxdProtocol/uxdClient.ts | 26 ++++++-------- utils/uiTypes/proposalCreationTypes.ts | 5 +++ 13 files changed, 157 insertions(+), 53 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx index 64531e7f16..503d99da1f 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx @@ -46,6 +46,7 @@ const DepositInsuranceToMangoDepository = ({ collateralMint: '', insuranceMint: '', insuranceDepositedAmount: 0, + controllerPda: '', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -68,12 +69,13 @@ const DepositInsuranceToMangoDepository = ({ wallet?.publicKey ) { const createIx = await createDepositInsuranceToMangoDepositoryInstruction( - connection.current, + connection, form.governedAccount?.governance.info.governedAccount, form.governedAccount?.governance.pubkey, new PublicKey(form.collateralMint), new PublicKey(form.insuranceMint), - form.insuranceDepositedAmount || 0 + form.insuranceDepositedAmount || 0, + new PublicKey(form.controllerPda) ) serializedInstruction = serializeInstructionToBase64(createIx) } @@ -175,6 +177,18 @@ const DepositInsuranceToMangoDepository = ({ } error={formErrors['global']} /> + + handleSetForm({ + value: evt.target.value, + propertyName: 'controllerPda', + }) + } + error={formErrors['controllerPda']} + /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx index 4ecfa09213..d1680d28a1 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx @@ -45,6 +45,7 @@ const RegisterMangoDepository = ({ programId: programId?.toString(), collateralMint: '', insuranceMint: '', + controllerPda: '', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -67,12 +68,13 @@ const RegisterMangoDepository = ({ wallet?.publicKey ) { const createIx = await createRegisterMangoDepositoryInstruction( - connection.current, + connection, form.governedAccount?.governance.info.governedAccount, form.governedAccount?.governance.pubkey, new PublicKey(wallet.publicKey.toBase58()), new PublicKey(form.collateralMint), - new PublicKey(form.insuranceMint) + new PublicKey(form.insuranceMint), + new PublicKey(form.controllerPda) ) serializedInstruction = serializeInstructionToBase64(createIx) } @@ -83,6 +85,7 @@ const RegisterMangoDepository = ({ } return obj } + useEffect(() => { handleSetForm({ propertyName: 'programId', @@ -98,6 +101,16 @@ const RegisterMangoDepository = ({ }) } }, [form.collateralMint]) + + useEffect(() => { + if (form.controllerPda) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.controllerPda]) + useEffect(() => { if (form.insuranceMint) { debounce.debounceFcn(async () => { @@ -106,17 +119,20 @@ const RegisterMangoDepository = ({ }) } }, [form.insuranceMint]) + useEffect(() => { handleSetInstructions( { governedAccount: form.governedAccount?.governance, getInstruction }, index ) }, [form]) + const schema = yup.object().shape({ collateralMint: yup .string() .required('Collateral Mint address is required'), insuranceMint: yup.string().required('Insurance Mint address is required'), + controllerPda: yup.string().required('Insurance Mint address is required'), governedAccount: yup .object() .nullable() @@ -151,7 +167,7 @@ const RegisterMangoDepository = ({ handleSetForm({ value: evt.target.value, @@ -160,6 +176,18 @@ const RegisterMangoDepository = ({ } error={formErrors['insuranceMint']} /> + + handleSetForm({ + value: evt.target.value, + propertyName: 'controllerPda', + }) + } + error={formErrors['controllerPda']} + /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx index fb6c698f86..76c90f6c4f 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -44,6 +44,7 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ governedAccount: undefined, programId: programId?.toString(), supplyCap: 0, + controllerPda: '', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -69,7 +70,8 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ connection.current, form.governedAccount.governance?.info.governedAccount, form.supplyCap || 9, - form.governedAccount?.governance.pubkey + form.governedAccount?.governance.pubkey, + new PublicKey(form.controllerPda) ) serializedInstruction = serializeInstructionToBase64(createIx) } @@ -103,6 +105,7 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ }, [form]) const schema = yup.object().shape({ supplyCap: yup.number().required('Redeemable supply cap is required'), + controllerAddress: yup.string().required('Controller address is required'), governedAccount: yup .object() .nullable() @@ -137,6 +140,18 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ } error={formErrors['global']} /> + + handleSetForm({ + value: evt.target.value, + propertyName: 'controllerPda', + }) + } + error={formErrors['controllerPda']} + /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx index c3074163ff..df886eda5c 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx @@ -44,6 +44,7 @@ const SetRedeemGlobalSupplyCap = ({ governedAccount: undefined, programId: programId?.toString(), supplyCap: 0, + controllerPda: '', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -69,7 +70,8 @@ const SetRedeemGlobalSupplyCap = ({ connection.current, form.governedAccount.governance?.info.governedAccount, form.supplyCap || 0, - form.governedAccount?.governance.pubkey + form.governedAccount?.governance.pubkey, + new PublicKey(form.controllerPda) ) serializedInstruction = serializeInstructionToBase64(createIx) } @@ -105,6 +107,7 @@ const SetRedeemGlobalSupplyCap = ({ supplyCap: yup .number() .required('Redeemable global supply cap is required'), + controllerPda: yup.string().required('Controller address is required'), governedAccount: yup .object() .nullable() @@ -139,6 +142,18 @@ const SetRedeemGlobalSupplyCap = ({ } error={formErrors['global']} /> + + handleSetForm({ + value: evt.target.value, + propertyName: 'controllerPda', + }) + } + error={formErrors['controllerPda']} + /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx index 30528540d2..2cbb831d61 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx @@ -46,6 +46,7 @@ const WithdrawInsuranceFromMangoDepository = ({ collateralMint: '', insuranceMint: '', insuranceWithdrawnAmount: 0, + controllerPda: '', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -68,12 +69,13 @@ const WithdrawInsuranceFromMangoDepository = ({ wallet?.publicKey ) { const createIx = await createWithdrawInsuranceFromMangoDepositoryInstruction( - connection.current, + connection, form.governedAccount?.governance.info.governedAccount, form.governedAccount?.governance.pubkey, new PublicKey(form.collateralMint), new PublicKey(form.insuranceMint), - form.insuranceWithdrawnAmount || 0 + form.insuranceWithdrawnAmount || 0, + new PublicKey(form.controllerPda) ) serializedInstruction = serializeInstructionToBase64(createIx) } @@ -175,6 +177,18 @@ const WithdrawInsuranceFromMangoDepository = ({ } error={formErrors['global']} /> + + handleSetForm({ + value: evt.target.value, + propertyName: 'controllerPda', + }) + } + error={formErrors['controllerPda']} + /> ) } diff --git a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts index 4e3b65a9bd..f7604758fc 100644 --- a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts @@ -1,19 +1,21 @@ import { Provider } from '@project-serum/anchor' -import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' -import { MangoDepository } from '@uxdprotocol/uxd-client' +import { TransactionInstruction, PublicKey } from '@solana/web3.js' +import { Controller, MangoDepository } from '@uxdprotocol/uxd-client' +import { ConnectionContext } from 'stores/useWalletStore' import { uxdClient, initializeMango } from './uxdClient' const createDepositInsuranceToMangoDepositoryInstruction = async ( - connection: Connection, + connection: ConnectionContext, uxdProgramId: PublicKey, authority: PublicKey, depositoryMint: PublicKey, insuranceMint: PublicKey, - insuranceDepositedAmount: number + insuranceDepositedAmount: number, + controllerPda: PublicKey ): Promise => { - const { client, controller } = uxdClient(connection, uxdProgramId) + const client = uxdClient(connection.current, uxdProgramId) - const mango = await initializeMango(connection) + const mango = await initializeMango(connection.current, connection.cluster) const depository = new MangoDepository( depositoryMint, 'collateralName', @@ -26,7 +28,7 @@ const createDepositInsuranceToMangoDepositoryInstruction = async ( return client.createDepositInsuranceToMangoDepositoryInstruction( insuranceDepositedAmount, - controller, + { pda: controllerPda } as Controller, depository, mango, authority, diff --git a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts index c73289d056..1fbe6ea746 100644 --- a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts +++ b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts @@ -1,5 +1,6 @@ import { Provider } from '@project-serum/anchor' import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { Controller } from '@uxdprotocol/uxd-client' import { uxdClient } from './uxdClient' const createInitializeControllerInstruction = ( @@ -9,9 +10,11 @@ const createInitializeControllerInstruction = ( payer: PublicKey, connection: Connection ): TransactionInstruction => { - const { client, controller } = uxdClient( + const client = uxdClient(connection, uxdProgramId) + + const controller = new Controller( + 'redeemableTicker', mintDecimals, - connection, uxdProgramId ) diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index 9b17fa5132..9fabedece6 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -1,20 +1,26 @@ import { serializeInstructionToBase64 } from '@models/serialisation' import { Provider } from '@project-serum/anchor' import { Token, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token' -import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { TOKEN_PROGRAM_ID } from '@utils/tokens' -import { findATAAddrSync, MangoDepository } from '@uxdprotocol/uxd-client' +import { + Controller, + findATAAddrSync, + MangoDepository, +} from '@uxdprotocol/uxd-client' +import { ConnectionContext } from 'stores/useWalletStore' import { initializeMango, uxdClient } from './uxdClient' const createRegisterMangoDepositoryInstruction = async ( - connection: Connection, + connection: ConnectionContext, uxdProgramId: PublicKey, authority: PublicKey, payer: PublicKey, collateralMint: PublicKey, - insuranceMint: PublicKey + insuranceMint: PublicKey, + controllerPda: PublicKey ): Promise => { - const mango = await initializeMango(connection) + const mango = await initializeMango(connection.current, connection.cluster) const depository = new MangoDepository( collateralMint, 'collateralName', @@ -25,7 +31,7 @@ const createRegisterMangoDepositoryInstruction = async ( uxdProgramId ) - const { client, controller } = uxdClient(connection, uxdProgramId) + const client = uxdClient(connection.current, uxdProgramId) const [authorityInsuranceATA] = findATAAddrSync(authority, insuranceMint) const createAuthorityInsuranceItx = Token.createAssociatedTokenAccountInstruction( ASSOCIATED_TOKEN_PROGRAM_ID, @@ -42,7 +48,7 @@ const createRegisterMangoDepositoryInstruction = async ( ) return client.createRegisterMangoDepositoryInstruction( - controller, + { pda: controllerPda } as Controller, depository, mango, authority, diff --git a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts index be1e02b25c..2bd2b6258c 100644 --- a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts @@ -1,17 +1,19 @@ import { Provider } from '@project-serum/anchor' import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { Controller } from '@uxdprotocol/uxd-client' import { uxdClient } from './uxdClient' const createSetMangoDepositoriesRedeemableSoftCapInstruction = ( connection: Connection, uxdProgramId: PublicKey, supplyCapUiAmount: number, - authority: PublicKey + authority: PublicKey, + controllerPda: PublicKey ): TransactionInstruction => { - const { client, controller } = uxdClient(connection, uxdProgramId) + const client = uxdClient(connection, uxdProgramId) return client.createSetMangoDepositoriesRedeemableSoftCapInstruction( - controller, + { pda: controllerPda } as Controller, authority, supplyCapUiAmount, Provider.defaultOptions() diff --git a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts index 66f183e46f..3354a672c9 100644 --- a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts @@ -1,16 +1,18 @@ import { Provider } from '@project-serum/anchor' import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { Controller } from '@uxdprotocol/uxd-client' import { uxdClient } from './uxdClient' const createSetRedeemableGlobalSupplyCapInstruction = ( connection: Connection, uxdProgramId: PublicKey, supplyCapUiAmount: number, - authority: PublicKey + authority: PublicKey, + controllerPda: PublicKey ): TransactionInstruction => { - const { client, controller } = uxdClient(connection, uxdProgramId) + const client = uxdClient(connection, uxdProgramId) return client.createSetRedeemableGlobalSupplyCapInstruction( - controller, + { pda: controllerPda } as Controller, authority, supplyCapUiAmount, Provider.defaultOptions() diff --git a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts index 58b96a0c98..3e799d3833 100644 --- a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts @@ -1,19 +1,21 @@ import { Provider } from '@project-serum/anchor' -import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' -import { MangoDepository } from '@uxdprotocol/uxd-client' +import { TransactionInstruction, PublicKey } from '@solana/web3.js' +import { Controller, MangoDepository } from '@uxdprotocol/uxd-client' +import { ConnectionContext } from 'stores/useWalletStore' import { uxdClient, initializeMango } from './uxdClient' const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( - connection: Connection, + connection: ConnectionContext, uxdProgramId: PublicKey, authority: PublicKey, depositoryMint: PublicKey, insuranceMint: PublicKey, - insuranceWithdrawnAmount: number + insuranceWithdrawnAmount: number, + controllerPda: PublicKey ): Promise => { - const { client, controller } = uxdClient(connection, uxdProgramId) + const client = uxdClient(connection.current, uxdProgramId) - const mango = await initializeMango(connection) + const mango = await initializeMango(connection.current, connection.cluster) const depository = new MangoDepository( depositoryMint, 'collateralName', @@ -26,7 +28,7 @@ const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( return client.createWithdrawInsuranceFromMangoDepositoryInstruction( insuranceWithdrawnAmount, - controller, + { pda: controllerPda } as Controller, depository, mango, authority, diff --git a/tools/sdk/uxdProtocol/uxdClient.ts b/tools/sdk/uxdProtocol/uxdClient.ts index f7699b7a39..832ab387ae 100644 --- a/tools/sdk/uxdProtocol/uxdClient.ts +++ b/tools/sdk/uxdProtocol/uxdClient.ts @@ -1,33 +1,29 @@ +import { EndpointTypes } from '@models/types' import { Program, Provider } from '@project-serum/anchor' import Wallet from '@project-serum/sol-wallet-adapter' import { Connection, PublicKey } from '@solana/web3.js' -import { - Controller, - createAndInitializeMango, - UXD, -} from '@uxdprotocol/uxd-client' +import { createAndInitializeMango, UXD } from '@uxdprotocol/uxd-client' import uxdIdl from './uxdIdl' const DEFAULT_WALLET_PROVIDER = 'https://sollet.io' -const CLUSTER = 'devnet' export const uxdClient = ( - mintDecimals: number, connection: Connection, programId: PublicKey -): { client: UXD; controller: Controller } => { +): UXD => { const wallet = new Wallet(DEFAULT_WALLET_PROVIDER) + const provider = new Provider(connection, wallet, Provider.defaultOptions()) - return { - client: new UXD(new Program(uxdIdl, programId, provider)), - controller: new Controller('redeemable_ticker', mintDecimals, programId), // The redeemable ticker is just local, doesn't matter. - } + return new UXD(new Program(uxdIdl, programId, provider)) } -export const initializeMango = async (connection: Connection) => { +export const initializeMango = async ( + connection: Connection, + cluster: EndpointTypes +) => { const wallet = new Wallet(DEFAULT_WALLET_PROVIDER) const provider = new Provider(connection, wallet, Provider.defaultOptions()) - return createAndInitializeMango(provider, CLUSTER) + return createAndInitializeMango(provider, cluster) } // import { Program, Provider } from '@project-serum/anchor' @@ -45,7 +41,7 @@ export const initializeMango = async (connection: Connection) => { // export const uxdClient = (): UXD => { // const connection = useWalletStore((s) => s.connection) -// const wallet = useWalletStore((s) => s.current) +// const wallet = useWalletStore((s) => s.current) // // PROBABLY NOT OK to fix // const provider = new Provider(connection.current, wallet, Provider.defaultOptions()); diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 5c6c72591e..d821a8109c 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -77,12 +77,14 @@ export interface SetRedeemableGlobalSupplyCapForm { governedAccount: GovernedProgramAccount | undefined supplyCap: number | undefined programId: string | undefined + controllerPda: string } export interface SetMangoDepositoriesRedeemableSoftCapForm { governedAccount: GovernedProgramAccount | undefined supplyCap: number | undefined programId: string | undefined + controllerPda: string } export interface RegisterMangoDepositoryForm { @@ -90,6 +92,7 @@ export interface RegisterMangoDepositoryForm { collateralMint: string insuranceMint: string programId: string | undefined + controllerPda: string } export interface DepositInsuranceToMangoDepositoryForm { @@ -98,6 +101,7 @@ export interface DepositInsuranceToMangoDepositoryForm { insuranceMint: string insuranceDepositedAmount: number | undefined programId: string | undefined + controllerPda: string } export interface WithdrawInsuranceFromMangoDepositoryForm { @@ -106,6 +110,7 @@ export interface WithdrawInsuranceFromMangoDepositoryForm { insuranceMint: string insuranceWithdrawnAmount: number | undefined programId: string | undefined + controllerPda: string } export enum UXDIntructions { From b296a8b9662df3f424b1493ecdd4a911f35e42f4 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 16 Dec 2021 04:48:05 +0900 Subject: [PATCH 036/204] update uxd-client to 3.1, update idl --- package.json | 2 +- public/realms/devnet.json | 4 +- tools/sdk/uxdProtocol/uxdIdl.ts | 84 ++++++++++++++++----------------- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/package.json b/package.json index ab631b9737..5fd178131e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "license": "MIT", "version": "1.0.0", "scripts": { - "dev": "next dev", + "dev": "next dev -p 3001", "build": "next build", "start": "next start", "prepare": "husky install", diff --git a/public/realms/devnet.json b/public/realms/devnet.json index 14af88a146..ca8f6b2962 100644 --- a/public/realms/devnet.json +++ b/public/realms/devnet.json @@ -100,8 +100,8 @@ { "symbol": "UXD", "displayName": "UXD Protocol DAO - devnet", - "programId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", - "realmId": "3eZ6p8VxnmncMgVYK1oMGb7rSiZPxoi6vtpDr3qKMEG3", + "programId": "UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa", + "realmId": "DhEy9AXcRsvMDucksP3XpHpmLsCpdvWJgU3LCyoryker", "ogImage": "/realms/UXD/img/logo.png", "website": "https://www.uxd.fi.com/", "twitter": "@UXDProtocol" diff --git a/tools/sdk/uxdProtocol/uxdIdl.ts b/tools/sdk/uxdProtocol/uxdIdl.ts index 7dc8ee7247..11563a8b9f 100644 --- a/tools/sdk/uxdProtocol/uxdIdl.ts +++ b/tools/sdk/uxdProtocol/uxdIdl.ts @@ -1,9 +1,9 @@ import { Idl } from '@project-serum/anchor' -export const UXD_PROGRAM_ID = 'UXDQDbkAeGMPR7gqDykDNu22D9DnYrKdvZhvNmMu6QX' +export const UXD_PROGRAM_ID = 'DdJ9BKWG8XqQaj5BMjYks6Y8Amcv1onoo7cvVipp9jKC' //'UXDQDbkAeGMPR7gqDykDNu22D9DnYrKdvZhvNmMu6QX' const uxdIdl: Idl = { - version: '1.2.0', + version: '0.0.0', name: 'uxd', instructions: [ { @@ -779,219 +779,219 @@ const uxdIdl: Idl = { ], errors: [ { - code: 6000, + code: 300, name: 'InvalidRedeemableMintDecimals', msg: 'The redeemable mint decimals must be between 0 and 9 (inclusive).', }, { - code: 6001, + code: 301, name: 'InvalidRedeemableGlobalSupplyCap', msg: 'The redeemable global supply cap must be below MAX_REDEEMABLE_GLOBAL_SUPPLY_CAP.', }, { - code: 6002, + code: 302, name: 'RootBankIndexNotFound', msg: 'The associated mango root bank index cannot be found for the deposited coin..', }, { - code: 6003, + code: 303, name: 'InvalidSlippage', msg: 'The slippage value is invalid. Must be in the [0...1000] range points.', }, { - code: 6004, + code: 304, name: 'InvalidCollateralMint', msg: "The provided collateral mint does not match the depository's collateral mint.", }, { - code: 6005, + code: 305, name: 'InvalidInsuranceMint', msg: "The provided insurance mint does not match the depository's insurance mint.", }, { - code: 6006, + code: 306, name: 'InvalidCollateralAmount', msg: 'Collateral amount must be > 0 in order to mint.', }, { - code: 6007, + code: 307, name: 'InsuficientCollateralAmount', msg: 'The balance of the collateral ATA is not enough to fulfill the mint operation.', }, { - code: 6008, + code: 308, name: 'InvalidRedeemableAmount', msg: 'The redeemable amount for redeem must be superior to 0.', }, { - code: 6009, + code: 309, name: 'InsuficientRedeemableAmount', msg: 'The balance of the redeemable ATA is not enough to fulfill the redeem operation.', }, { - code: 6010, + code: 310, name: 'InvalidAuthority', msg: 'Only the Program initializer authority can access this instructions.', }, { - code: 6011, + code: 311, name: 'InvalidRedeemableMint', msg: "The Redeemable Mint provided does not match the Controller's one.", }, { - code: 6012, + code: 312, name: 'InvalidUserRedeemableATAMint', msg: "The user's Redeemable ATA's mint does not match the Controller's one.", }, { - code: 6013, + code: 313, name: 'InvalidUserCollateralATAMint', msg: "The user's Collateral ATA's mint does not match the Depository's one.", }, { - code: 6014, + code: 314, name: 'InvalidAuthorityInsuranceATAMint', msg: "The authority's Insurance ATA's mint does not match the Depository's one.", }, { - code: 6015, + code: 315, name: 'InvalidCollateralPassthroughATAMint', msg: "The Depository Collateral Passthrough ATA's mint does not match the Depository's one.", }, { - code: 6016, + code: 316, name: 'InvalidInsurancePassthroughATAMint', msg: "The Depository Insurance Passthrough ATA's mint does not match the Depository's one.", }, { - code: 6017, + code: 317, name: 'PerpOrderPartiallyFilled', msg: 'The perp position could not be fully filled with the provided slippage.', }, { - code: 6018, + code: 318, name: 'PositionAmountCalculation', msg: 'Error while getting the redeemable value of the deposited coin amount.', }, { - code: 6019, + code: 319, name: 'RedeemableGlobalSupplyCapReached', msg: 'Minting amount would go past the Redeemable Global Supply Cap.', }, { - code: 6020, + code: 320, name: 'MangoDepositoriesSoftCapOverflow', msg: 'Operation not allowed due to being over the Redeemable soft Cap.', }, { - code: 6030, + code: 330, name: 'MaxNumberOfMangoDepositoriesRegisteredReached', msg: 'Cannot register more mango depositories, the limit has been reached.', }, { - code: 6031, + code: 331, name: 'InvalidController', msg: "The Depository's controller doesn't match the provided Controller.", }, { - code: 6032, + code: 332, name: 'InvalidDepository', msg: 'The Depository provided is not registered with the Controller.', }, { - code: 6033, + code: 333, name: 'InvalidCollateralPassthroughAccount', msg: "The Collateral Passthrough Account isn't the Deposiroty one.", }, { - code: 6034, + code: 334, name: 'InvalidInsurancePassthroughAccount', msg: "The Insurance Passthrough Account isn't the Deposiroty one.", }, { - code: 6035, + code: 335, name: 'InvalidMangoAccount', msg: "The Mango Account isn't the Deposiroty one.", }, { - code: 6036, + code: 336, name: 'InvalidInsuranceAmount', msg: 'The amount to withdraw from the Insurance Fund must be superior to zero..', }, { - code: 6037, + code: 337, name: 'InsuficientAuthorityInsuranceAmount', msg: "The Insurance ATA from authority doesn't have enough balance.", }, { - code: 6038, + code: 338, name: 'InvalidRebalancingAmount', msg: 'The max amount to rebalance must be superior to zero..', }, { - code: 6039, + code: 339, name: 'InsuficentOrderBookDepth', msg: 'Insuficcent order book depth for order.', }, { - code: 6040, + code: 340, name: 'InvalidExecutedOrderSize', msg: 'The executed order size does not match the expected one.', }, { - code: 6080, + code: 380, name: 'MangoOrderBookLoading', msg: 'Could not load Mango Order book.', }, { - code: 6081, + code: 381, name: 'MangoGroupLoading', msg: 'Could not load Mango Group.', }, { - code: 6082, + code: 382, name: 'MangoCacheLoading', msg: 'Could not load Mango Cache.', }, { - code: 6083, + code: 383, name: 'MangoLoadPerpMarket', msg: 'Could not load Mango PerpMarket.', }, { - code: 6084, + code: 384, name: 'MangoAccountLoading', msg: 'Could not load Mango Account.', }, { - code: 6085, + code: 385, name: 'MangoPerpMarketIndexNotFound', msg: 'Could not find the perp market index for the given collateral.', }, { - code: 6086, + code: 386, name: 'InvalidPerpAccountState', msg: 'The Mango PerpAccount has uncommitted changes.', }, { - code: 6087, + code: 387, name: 'InvalidDepositoryAccounting', msg: 'The Depository accounting is in an invalid state.', }, From 327953e68379b9889b6cfc1407b6b7d83488151b Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 16 Dec 2021 22:49:26 +0900 Subject: [PATCH 037/204] reinforce input rules, use wallet for provider, small uxd-client refactor --- .env.sample | 4 +- .../DepositInsuranceToMangoDepository.tsx | 32 ++++++++++-- .../instructions/InitializeController.tsx | 9 +++- .../instructions/RegisterMangoDepository.tsx | 5 +- .../SetMangoDepositoriesRedeemableSoftCap.tsx | 26 ++++++++-- .../instructions/SetRedeemGlobalSupplyCap.tsx | 22 ++++++-- .../WithdrawInsuranceFromMangoDepository.tsx | 34 +++++++++++-- ...itInsuranceToMangoDepositoryInstruction.ts | 30 ++++++----- .../createInitializeControllerInstruction.ts | 17 +++---- ...reateRegisterMangoDepositoryInstruction.ts | 38 +++++++------- ...epositoriesRedeemableSoftCapInstruction.ts | 6 ++- ...SetRedeemableGlobalSupplyCapInstruction.ts | 7 ++- ...InsuranceFromMangoDepositoryInstruction.ts | 30 ++++++----- tools/sdk/uxdProtocol/uxdClient.ts | 50 ++++++++++++++++--- utils/uiTypes/proposalCreationTypes.ts | 10 ++-- 15 files changed, 228 insertions(+), 92 deletions(-) diff --git a/.env.sample b/.env.sample index 4091f1b5cf..bae5e9f083 100644 --- a/.env.sample +++ b/.env.sample @@ -1,4 +1,4 @@ # Run in context of one realm: starts with default realm and disables realms page navigation # REALM=MNGO -MAINNET_RPC=https://mango.rpcpool.com -DEVNET_RPC=https://mango.devnet.rpcpool.com \ No newline at end of file +#MAINNET_RPC=https://mango.rpcpool.com +#DEVNET_RPC=https://mango.devnet.rpcpool.com \ No newline at end of file diff --git a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx index 503d99da1f..dcd718b2ce 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx @@ -74,8 +74,9 @@ const DepositInsuranceToMangoDepository = ({ form.governedAccount?.governance.pubkey, new PublicKey(form.collateralMint), new PublicKey(form.insuranceMint), - form.insuranceDepositedAmount || 0, - new PublicKey(form.controllerPda) + form.insuranceDepositedAmount, + new PublicKey(form.controllerPda), + wallet ) serializedInstruction = serializeInstructionToBase64(createIx) } @@ -101,6 +102,7 @@ const DepositInsuranceToMangoDepository = ({ }) } }, [form.collateralMint]) + useEffect(() => { if (form.insuranceMint) { debounce.debounceFcn(async () => { @@ -109,6 +111,25 @@ const DepositInsuranceToMangoDepository = ({ }) } }, [form.insuranceMint]) + + useEffect(() => { + if (form.insuranceDepositedAmount) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.insuranceDepositedAmount]) + + useEffect(() => { + if (form.controllerPda) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.controllerPda]) + useEffect(() => { handleSetInstructions( { governedAccount: form.governedAccount?.governance, getInstruction }, @@ -120,6 +141,11 @@ const DepositInsuranceToMangoDepository = ({ .string() .required('Collateral Mint address is required'), insuranceMint: yup.string().required('Insurance Mint address is required'), + controllerPda: yup.string().required('Controller address is required'), + insuranceDepositedAmount: yup + .number() + .moreThan(0, 'Insurance Deposited amount should be more than 0') + .required('Insurance Deposited amount is required'), governedAccount: yup .object() .nullable() @@ -175,7 +201,7 @@ const DepositInsuranceToMangoDepository = ({ propertyName: 'insuranceDepositedAmount', }) } - error={formErrors['global']} + error={formErrors['insuranceDepositedAmount']} /> { const isValid = await validateInstruction() let serializedInstruction = '' + if ( isValid && programId && @@ -69,9 +70,10 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ const createIx = createSetMangoDepositoriesRedeemableSoftCapInstruction( connection.current, form.governedAccount.governance?.info.governedAccount, - form.supplyCap || 9, + form.supplyCap, form.governedAccount?.governance.pubkey, - new PublicKey(form.controllerPda) + new PublicKey(form.controllerPda), + wallet ) serializedInstruction = serializeInstructionToBase64(createIx) } @@ -82,6 +84,7 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ } return obj } + useEffect(() => { handleSetForm({ propertyName: 'programId', @@ -97,14 +100,28 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ }) } }, [form.supplyCap]) + + useEffect(() => { + if (form.controllerPda) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.controllerPda]) + useEffect(() => { handleSetInstructions( { governedAccount: form.governedAccount?.governance, getInstruction }, index ) }, [form]) + const schema = yup.object().shape({ - supplyCap: yup.number().required('Redeemable supply cap is required'), + supplyCap: yup + .number() + .moreThan(0, 'Redeemable supply cap should be more than 0') + .required('Redeemable supply cap is required'), controllerAddress: yup.string().required('Controller address is required'), governedAccount: yup .object() @@ -131,14 +148,13 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ value={form.supplyCap} type="number" min={0} - max={10 ** 12} onChange={(evt) => handleSetForm({ value: evt.target.value, propertyName: 'supplyCap', }) } - error={formErrors['global']} + error={formErrors['supplyCap']} /> { handleSetForm({ propertyName: 'programId', @@ -97,15 +99,27 @@ const SetRedeemGlobalSupplyCap = ({ }) } }, [form.supplyCap]) + + useEffect(() => { + if (form.controllerPda) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.controllerPda]) + useEffect(() => { handleSetInstructions( { governedAccount: form.governedAccount?.governance, getInstruction }, index ) }, [form]) + const schema = yup.object().shape({ supplyCap: yup .number() + .moreThan(0, 'Redeemable global supply cap should be more than 0') .required('Redeemable global supply cap is required'), controllerPda: yup.string().required('Controller address is required'), governedAccount: yup @@ -133,15 +147,15 @@ const SetRedeemGlobalSupplyCap = ({ value={form.supplyCap} type="number" min={0} - max={10 ** 12} onChange={(evt) => handleSetForm({ value: evt.target.value, propertyName: 'supplyCap', }) } - error={formErrors['global']} + error={formErrors['supplyCap']} /> + { if (form.insuranceMint) { debounce.debounceFcn(async () => { @@ -109,17 +111,42 @@ const WithdrawInsuranceFromMangoDepository = ({ }) } }, [form.insuranceMint]) + + useEffect(() => { + if (form.insuranceWithdrawnAmount) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.insuranceWithdrawnAmount]) + + useEffect(() => { + if (form.controllerPda) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.controllerPda]) + useEffect(() => { handleSetInstructions( { governedAccount: form.governedAccount?.governance, getInstruction }, index ) }, [form]) + const schema = yup.object().shape({ collateralMint: yup .string() .required('Collateral Mint address is required'), insuranceMint: yup.string().required('Insurance Mint address is required'), + controllerPda: yup.string().required('Controller address is required'), + insuranceWithdrawnAmount: yup + .number() + .moreThan(0, 'Insurance Withdrawn amount should be more than 0') + .required('Insurance Withdrawn amount is required'), governedAccount: yup .object() .nullable() @@ -168,14 +195,13 @@ const WithdrawInsuranceFromMangoDepository = ({ value={form.insuranceWithdrawnAmount} type="number" min={0} - max={10 ** 12} onChange={(evt) => handleSetForm({ value: evt.target.value, propertyName: 'insuranceWithdrawnAmount', }) } - error={formErrors['global']} + error={formErrors['insuranceWithdrawnAmount']} /> => { - const client = uxdClient(connection.current, uxdProgramId) + const client = uxdClient(connection.current, uxdProgramId, wallet) - const mango = await initializeMango(connection.current, connection.cluster) - const depository = new MangoDepository( + const mango = await initializeMango( + connection.current, + connection.cluster, + wallet + ) + const depository = instantiateMangoDepository( + uxdProgramId, depositoryMint, - 'collateralName', - 6, - insuranceMint, - 'USDC', - 6, - uxdProgramId + insuranceMint ) return client.createDepositInsuranceToMangoDepositoryInstruction( diff --git a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts index 1fbe6ea746..c206ca9f1f 100644 --- a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts +++ b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts @@ -1,25 +1,20 @@ import { Provider } from '@project-serum/anchor' +import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' -import { Controller } from '@uxdprotocol/uxd-client' -import { uxdClient } from './uxdClient' +import { instantiateController, uxdClient } from './uxdClient' const createInitializeControllerInstruction = ( uxdProgramId: PublicKey, mintDecimals: number, authority: PublicKey, payer: PublicKey, - connection: Connection + connection: Connection, + wallet: SignerWalletAdapter ): TransactionInstruction => { - const client = uxdClient(connection, uxdProgramId) - - const controller = new Controller( - 'redeemableTicker', - mintDecimals, - uxdProgramId - ) + const client = uxdClient(connection, uxdProgramId, wallet) return client.createInitializeControllerInstruction( - controller, + instantiateController(uxdProgramId, mintDecimals), authority, Provider.defaultOptions(), payer diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index 9fabedece6..d7b96339ee 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -1,37 +1,39 @@ import { serializeInstructionToBase64 } from '@models/serialisation' import { Provider } from '@project-serum/anchor' import { Token, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token' +import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { TOKEN_PROGRAM_ID } from '@utils/tokens' -import { - Controller, - findATAAddrSync, - MangoDepository, -} from '@uxdprotocol/uxd-client' +import { Controller, findATAAddrSync } from '@uxdprotocol/uxd-client' import { ConnectionContext } from 'stores/useWalletStore' -import { initializeMango, uxdClient } from './uxdClient' +import { + initializeMango, + instantiateMangoDepository, + uxdClient, +} from './uxdClient' const createRegisterMangoDepositoryInstruction = async ( connection: ConnectionContext, uxdProgramId: PublicKey, authority: PublicKey, payer: PublicKey, - collateralMint: PublicKey, + depositoryMint: PublicKey, insuranceMint: PublicKey, - controllerPda: PublicKey + controllerPda: PublicKey, + wallet: SignerWalletAdapter ): Promise => { - const mango = await initializeMango(connection.current, connection.cluster) - const depository = new MangoDepository( - collateralMint, - 'collateralName', - 6, - insuranceMint, - 'USDC', - 6, - uxdProgramId + const mango = await initializeMango( + connection.current, + connection.cluster, + wallet + ) + const depository = instantiateMangoDepository( + uxdProgramId, + depositoryMint, + insuranceMint ) - const client = uxdClient(connection.current, uxdProgramId) + const client = uxdClient(connection.current, uxdProgramId, wallet) const [authorityInsuranceATA] = findATAAddrSync(authority, insuranceMint) const createAuthorityInsuranceItx = Token.createAssociatedTokenAccountInstruction( ASSOCIATED_TOKEN_PROGRAM_ID, diff --git a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts index 2bd2b6258c..38eae0ce93 100644 --- a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts @@ -1,4 +1,5 @@ import { Provider } from '@project-serum/anchor' +import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' import { Controller } from '@uxdprotocol/uxd-client' import { uxdClient } from './uxdClient' @@ -8,9 +9,10 @@ const createSetMangoDepositoriesRedeemableSoftCapInstruction = ( uxdProgramId: PublicKey, supplyCapUiAmount: number, authority: PublicKey, - controllerPda: PublicKey + controllerPda: PublicKey, + wallet: SignerWalletAdapter ): TransactionInstruction => { - const client = uxdClient(connection, uxdProgramId) + const client = uxdClient(connection, uxdProgramId, wallet) return client.createSetMangoDepositoriesRedeemableSoftCapInstruction( { pda: controllerPda } as Controller, diff --git a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts index 3354a672c9..40a70271af 100644 --- a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts @@ -1,4 +1,5 @@ import { Provider } from '@project-serum/anchor' +import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' import { Controller } from '@uxdprotocol/uxd-client' import { uxdClient } from './uxdClient' @@ -8,9 +9,11 @@ const createSetRedeemableGlobalSupplyCapInstruction = ( uxdProgramId: PublicKey, supplyCapUiAmount: number, authority: PublicKey, - controllerPda: PublicKey + controllerPda: PublicKey, + wallet: SignerWalletAdapter ): TransactionInstruction => { - const client = uxdClient(connection, uxdProgramId) + const client = uxdClient(connection, uxdProgramId, wallet) + return client.createSetRedeemableGlobalSupplyCapInstruction( { pda: controllerPda } as Controller, authority, diff --git a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts index 3e799d3833..efefbdfa61 100644 --- a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts @@ -1,8 +1,13 @@ import { Provider } from '@project-serum/anchor' +import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey } from '@solana/web3.js' -import { Controller, MangoDepository } from '@uxdprotocol/uxd-client' +import { Controller } from '@uxdprotocol/uxd-client' import { ConnectionContext } from 'stores/useWalletStore' -import { uxdClient, initializeMango } from './uxdClient' +import { + uxdClient, + initializeMango, + instantiateMangoDepository, +} from './uxdClient' const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( connection: ConnectionContext, @@ -11,19 +16,20 @@ const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( depositoryMint: PublicKey, insuranceMint: PublicKey, insuranceWithdrawnAmount: number, - controllerPda: PublicKey + controllerPda: PublicKey, + wallet: SignerWalletAdapter ): Promise => { - const client = uxdClient(connection.current, uxdProgramId) + const client = uxdClient(connection.current, uxdProgramId, wallet) - const mango = await initializeMango(connection.current, connection.cluster) - const depository = new MangoDepository( + const mango = await initializeMango( + connection.current, + connection.cluster, + wallet + ) + const depository = instantiateMangoDepository( + uxdProgramId, depositoryMint, - 'collateralName', - 6, - insuranceMint, - 'USDC', - 6, - uxdProgramId + insuranceMint ) return client.createWithdrawInsuranceFromMangoDepositoryInstruction( diff --git a/tools/sdk/uxdProtocol/uxdClient.ts b/tools/sdk/uxdProtocol/uxdClient.ts index 832ab387ae..ab00929b9d 100644 --- a/tools/sdk/uxdProtocol/uxdClient.ts +++ b/tools/sdk/uxdProtocol/uxdClient.ts @@ -2,30 +2,64 @@ import { EndpointTypes } from '@models/types' import { Program, Provider } from '@project-serum/anchor' import Wallet from '@project-serum/sol-wallet-adapter' import { Connection, PublicKey } from '@solana/web3.js' -import { createAndInitializeMango, UXD } from '@uxdprotocol/uxd-client' +import { + Controller, + createAndInitializeMango, + MangoDepository, + UXD, +} from '@uxdprotocol/uxd-client' import uxdIdl from './uxdIdl' -const DEFAULT_WALLET_PROVIDER = 'https://sollet.io' - export const uxdClient = ( connection: Connection, - programId: PublicKey + programId: PublicKey, + wallet: Wallet ): UXD => { - const wallet = new Wallet(DEFAULT_WALLET_PROVIDER) - const provider = new Provider(connection, wallet, Provider.defaultOptions()) return new UXD(new Program(uxdIdl, programId, provider)) } export const initializeMango = async ( connection: Connection, - cluster: EndpointTypes + cluster: EndpointTypes, + wallet: Wallet ) => { - const wallet = new Wallet(DEFAULT_WALLET_PROVIDER) const provider = new Provider(connection, wallet, Provider.defaultOptions()) return createAndInitializeMango(provider, cluster) } +// We do not need the mint symbol so it is just set with a placeholder value +export const instantiateController = ( + uxdProgramId: PublicKey, + mintDecimals: number, + mintSymbol = 'UXD' +) => { + return new Controller(mintSymbol, mintDecimals, uxdProgramId) +} + +// We do not need the decimals and names for both depository and insurance +// in order to register a new mango depository +// we just set placeholder values +export const instantiateMangoDepository = ( + uxdProgramId: PublicKey, + depositoryMint: PublicKey, + insuranceMint: PublicKey, + depositoryName = 'collateral', + depositoryDecimals = 6, + insuranceName = 'insurance', + insuranceDecimals = 6 +): MangoDepository => { + return new MangoDepository( + depositoryMint, + depositoryName, + depositoryDecimals, + insuranceMint, + insuranceName, + insuranceDecimals, + uxdProgramId + ) +} + // import { Program, Provider } from '@project-serum/anchor' // import Wallet from '@project-serum/sol-wallet-adapter' // import { Connection } from '@solana/web3.js' diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index d821a8109c..fa208f12f2 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -69,20 +69,20 @@ export enum Instructions { export interface InitializeControllerForm { governedAccount: GovernedProgramAccount | undefined - mintDecimals: number | undefined + mintDecimals: number programId: string | undefined } export interface SetRedeemableGlobalSupplyCapForm { governedAccount: GovernedProgramAccount | undefined - supplyCap: number | undefined + supplyCap: number programId: string | undefined controllerPda: string } export interface SetMangoDepositoriesRedeemableSoftCapForm { governedAccount: GovernedProgramAccount | undefined - supplyCap: number | undefined + supplyCap: number programId: string | undefined controllerPda: string } @@ -99,7 +99,7 @@ export interface DepositInsuranceToMangoDepositoryForm { governedAccount: GovernedProgramAccount | undefined collateralMint: string insuranceMint: string - insuranceDepositedAmount: number | undefined + insuranceDepositedAmount: number programId: string | undefined controllerPda: string } @@ -108,7 +108,7 @@ export interface WithdrawInsuranceFromMangoDepositoryForm { governedAccount: GovernedProgramAccount | undefined collateralMint: string insuranceMint: string - insuranceWithdrawnAmount: number | undefined + insuranceWithdrawnAmount: number programId: string | undefined controllerPda: string } From 4c4bbd8a3e7b4fdba2ca72b69db7aba33e6e32cf Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 17 Dec 2021 01:18:52 +0900 Subject: [PATCH 038/204] small brushup --- .../SetMangoDepositoriesRedeemableSoftCap.tsx | 22 +++++++++---------- public/realms/devnet.json | 4 ++-- ...epositoriesRedeemableSoftCapInstruction.ts | 4 ++-- utils/uiTypes/proposalCreationTypes.ts | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx index b1df5d847f..41422a5c41 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -43,7 +43,7 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ const [form, setForm] = useState({ governedAccount: undefined, programId: programId?.toString(), - supplyCap: 0, + softCap: 0, controllerPda: '', }) const [formErrors, setFormErrors] = useState({}) @@ -70,7 +70,7 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ const createIx = createSetMangoDepositoriesRedeemableSoftCapInstruction( connection.current, form.governedAccount.governance?.info.governedAccount, - form.supplyCap, + form.softCap, form.governedAccount?.governance.pubkey, new PublicKey(form.controllerPda), wallet @@ -93,13 +93,13 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ }, [realmInfo?.programId]) useEffect(() => { - if (form.supplyCap) { + if (form.softCap) { debounce.debounceFcn(async () => { const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) }) } - }, [form.supplyCap]) + }, [form.softCap]) useEffect(() => { if (form.controllerPda) { @@ -118,11 +118,11 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ }, [form]) const schema = yup.object().shape({ - supplyCap: yup + softCap: yup .number() - .moreThan(0, 'Redeemable supply cap should be more than 0') - .required('Redeemable supply cap is required'), - controllerAddress: yup.string().required('Controller address is required'), + .moreThan(0, 'Redeemable soft cap should be more than 0') + .required('Redeemable soft cap is required'), + controllerPda: yup.string().required('Controller address is required'), governedAccount: yup .object() .nullable() @@ -145,16 +145,16 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ handleSetForm({ value: evt.target.value, - propertyName: 'supplyCap', + propertyName: 'softCap', }) } - error={formErrors['supplyCap']} + error={formErrors['softCap']} /> Date: Sat, 18 Dec 2021 15:26:03 +0900 Subject: [PATCH 039/204] controller Pda automatically derived instead of input --- .env | 7 ++++++ .../DepositInsuranceToMangoDepository.tsx | 24 ------------------ .../instructions/RegisterMangoDepository.tsx | 24 ------------------ .../SetMangoDepositoriesRedeemableSoftCap.tsx | 24 ------------------ .../instructions/SetRedeemGlobalSupplyCap.tsx | 25 ------------------- .../WithdrawInsuranceFromMangoDepository.tsx | 24 ------------------ ...itInsuranceToMangoDepositoryInstruction.ts | 4 +-- ...reateRegisterMangoDepositoryInstruction.ts | 4 +-- ...epositoriesRedeemableSoftCapInstruction.ts | 5 ++-- ...SetRedeemableGlobalSupplyCapInstruction.ts | 5 ++-- ...InsuranceFromMangoDepositoryInstruction.ts | 4 +-- tools/sdk/uxdProtocol/uxdClient.ts | 5 ++++ utils/uiTypes/proposalCreationTypes.ts | 5 ---- 13 files changed, 22 insertions(+), 138 deletions(-) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000000..4d5316a723 --- /dev/null +++ b/.env @@ -0,0 +1,7 @@ +# Run in context of one realm: starts with default realm and disables realms page navigation +REALM=UXD +MAINNET_RPC=https://billowing-proud-glitter.solana-mainnet.quiknode.pro/7f49d7f436c2d3af0738270d90dee86962f13a82/ +DEVNET_RPC=https://api.devnet.solana.com +UXD_PROGRAM_ID=UXDQDbkAeGMPR7gqDykDNu22D9DnYrKdvZhvNmMu6QX +GOVERNANCE_PROGRAM_ID=UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa + diff --git a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx index dcd718b2ce..4a8b917104 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx @@ -46,7 +46,6 @@ const DepositInsuranceToMangoDepository = ({ collateralMint: '', insuranceMint: '', insuranceDepositedAmount: 0, - controllerPda: '', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -75,7 +74,6 @@ const DepositInsuranceToMangoDepository = ({ new PublicKey(form.collateralMint), new PublicKey(form.insuranceMint), form.insuranceDepositedAmount, - new PublicKey(form.controllerPda), wallet ) serializedInstruction = serializeInstructionToBase64(createIx) @@ -121,15 +119,6 @@ const DepositInsuranceToMangoDepository = ({ } }, [form.insuranceDepositedAmount]) - useEffect(() => { - if (form.controllerPda) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.controllerPda]) - useEffect(() => { handleSetInstructions( { governedAccount: form.governedAccount?.governance, getInstruction }, @@ -141,7 +130,6 @@ const DepositInsuranceToMangoDepository = ({ .string() .required('Collateral Mint address is required'), insuranceMint: yup.string().required('Insurance Mint address is required'), - controllerPda: yup.string().required('Controller address is required'), insuranceDepositedAmount: yup .number() .moreThan(0, 'Insurance Deposited amount should be more than 0') @@ -203,18 +191,6 @@ const DepositInsuranceToMangoDepository = ({ } error={formErrors['insuranceDepositedAmount']} /> - - handleSetForm({ - value: evt.target.value, - propertyName: 'controllerPda', - }) - } - error={formErrors['controllerPda']} - /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx index 8809bce768..fe5c5f61cb 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx @@ -45,7 +45,6 @@ const RegisterMangoDepository = ({ programId: programId?.toString(), collateralMint: '', insuranceMint: '', - controllerPda: '', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -74,7 +73,6 @@ const RegisterMangoDepository = ({ new PublicKey(wallet.publicKey.toBase58()), new PublicKey(form.collateralMint), new PublicKey(form.insuranceMint), - new PublicKey(form.controllerPda), wallet ) serializedInstruction = serializeInstructionToBase64(createIx) @@ -103,15 +101,6 @@ const RegisterMangoDepository = ({ } }, [form.collateralMint]) - useEffect(() => { - if (form.controllerPda) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.controllerPda]) - useEffect(() => { if (form.insuranceMint) { debounce.debounceFcn(async () => { @@ -133,7 +122,6 @@ const RegisterMangoDepository = ({ .string() .required('Collateral Mint address is required'), insuranceMint: yup.string().required('Insurance Mint address is required'), - controllerPda: yup.string().required('Controller address is required'), governedAccount: yup .object() .nullable() @@ -177,18 +165,6 @@ const RegisterMangoDepository = ({ } error={formErrors['insuranceMint']} /> - - handleSetForm({ - value: evt.target.value, - propertyName: 'controllerPda', - }) - } - error={formErrors['controllerPda']} - /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx index 41422a5c41..50ea77bd68 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -44,7 +44,6 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ governedAccount: undefined, programId: programId?.toString(), softCap: 0, - controllerPda: '', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -72,7 +71,6 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ form.governedAccount.governance?.info.governedAccount, form.softCap, form.governedAccount?.governance.pubkey, - new PublicKey(form.controllerPda), wallet ) serializedInstruction = serializeInstructionToBase64(createIx) @@ -101,15 +99,6 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ } }, [form.softCap]) - useEffect(() => { - if (form.controllerPda) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.controllerPda]) - useEffect(() => { handleSetInstructions( { governedAccount: form.governedAccount?.governance, getInstruction }, @@ -122,7 +111,6 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ .number() .moreThan(0, 'Redeemable soft cap should be more than 0') .required('Redeemable soft cap is required'), - controllerPda: yup.string().required('Controller address is required'), governedAccount: yup .object() .nullable() @@ -156,18 +144,6 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ } error={formErrors['softCap']} /> - - handleSetForm({ - value: evt.target.value, - propertyName: 'controllerPda', - }) - } - error={formErrors['controllerPda']} - /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx index b2da7186e2..624107fe81 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx @@ -44,7 +44,6 @@ const SetRedeemGlobalSupplyCap = ({ governedAccount: undefined, programId: programId?.toString(), supplyCap: 0, - controllerPda: '', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -71,7 +70,6 @@ const SetRedeemGlobalSupplyCap = ({ form.governedAccount.governance?.info.governedAccount, form.supplyCap, form.governedAccount?.governance.pubkey, - new PublicKey(form.controllerPda), wallet ) serializedInstruction = serializeInstructionToBase64(createIx) @@ -100,15 +98,6 @@ const SetRedeemGlobalSupplyCap = ({ } }, [form.supplyCap]) - useEffect(() => { - if (form.controllerPda) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.controllerPda]) - useEffect(() => { handleSetInstructions( { governedAccount: form.governedAccount?.governance, getInstruction }, @@ -121,7 +110,6 @@ const SetRedeemGlobalSupplyCap = ({ .number() .moreThan(0, 'Redeemable global supply cap should be more than 0') .required('Redeemable global supply cap is required'), - controllerPda: yup.string().required('Controller address is required'), governedAccount: yup .object() .nullable() @@ -155,19 +143,6 @@ const SetRedeemGlobalSupplyCap = ({ } error={formErrors['supplyCap']} /> - - - handleSetForm({ - value: evt.target.value, - propertyName: 'controllerPda', - }) - } - error={formErrors['controllerPda']} - /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx index ef202754a1..77596e1250 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx @@ -46,7 +46,6 @@ const WithdrawInsuranceFromMangoDepository = ({ collateralMint: '', insuranceMint: '', insuranceWithdrawnAmount: 0, - controllerPda: '', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -75,7 +74,6 @@ const WithdrawInsuranceFromMangoDepository = ({ new PublicKey(form.collateralMint), new PublicKey(form.insuranceMint), form.insuranceWithdrawnAmount, - new PublicKey(form.controllerPda), wallet ) serializedInstruction = serializeInstructionToBase64(createIx) @@ -121,15 +119,6 @@ const WithdrawInsuranceFromMangoDepository = ({ } }, [form.insuranceWithdrawnAmount]) - useEffect(() => { - if (form.controllerPda) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.controllerPda]) - useEffect(() => { handleSetInstructions( { governedAccount: form.governedAccount?.governance, getInstruction }, @@ -142,7 +131,6 @@ const WithdrawInsuranceFromMangoDepository = ({ .string() .required('Collateral Mint address is required'), insuranceMint: yup.string().required('Insurance Mint address is required'), - controllerPda: yup.string().required('Controller address is required'), insuranceWithdrawnAmount: yup .number() .moreThan(0, 'Insurance Withdrawn amount should be more than 0') @@ -203,18 +191,6 @@ const WithdrawInsuranceFromMangoDepository = ({ } error={formErrors['insuranceWithdrawnAmount']} /> - - handleSetForm({ - value: evt.target.value, - propertyName: 'controllerPda', - }) - } - error={formErrors['controllerPda']} - /> ) } diff --git a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts index b8e001c71c..1081def987 100644 --- a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts @@ -7,6 +7,7 @@ import { uxdClient, initializeMango, instantiateMangoDepository, + getControllerPda, } from './uxdClient' const createDepositInsuranceToMangoDepositoryInstruction = async ( @@ -16,7 +17,6 @@ const createDepositInsuranceToMangoDepositoryInstruction = async ( depositoryMint: PublicKey, insuranceMint: PublicKey, insuranceDepositedAmount: number, - controllerPda: PublicKey, wallet: SignerWalletAdapter ): Promise => { const client = uxdClient(connection.current, uxdProgramId, wallet) @@ -34,7 +34,7 @@ const createDepositInsuranceToMangoDepositoryInstruction = async ( return client.createDepositInsuranceToMangoDepositoryInstruction( insuranceDepositedAmount, - { pda: controllerPda } as Controller, + { pda: getControllerPda(uxdProgramId) } as Controller, depository, mango, authority, diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index d7b96339ee..f0cf21beb2 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -7,6 +7,7 @@ import { TOKEN_PROGRAM_ID } from '@utils/tokens' import { Controller, findATAAddrSync } from '@uxdprotocol/uxd-client' import { ConnectionContext } from 'stores/useWalletStore' import { + getControllerPda, initializeMango, instantiateMangoDepository, uxdClient, @@ -19,7 +20,6 @@ const createRegisterMangoDepositoryInstruction = async ( payer: PublicKey, depositoryMint: PublicKey, insuranceMint: PublicKey, - controllerPda: PublicKey, wallet: SignerWalletAdapter ): Promise => { const mango = await initializeMango( @@ -50,7 +50,7 @@ const createRegisterMangoDepositoryInstruction = async ( ) return client.createRegisterMangoDepositoryInstruction( - { pda: controllerPda } as Controller, + { pda: getControllerPda(uxdProgramId) } as Controller, depository, mango, authority, diff --git a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts index fab463a666..33923699f1 100644 --- a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts @@ -2,20 +2,19 @@ import { Provider } from '@project-serum/anchor' import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' import { Controller } from '@uxdprotocol/uxd-client' -import { uxdClient } from './uxdClient' +import { getControllerPda, uxdClient } from './uxdClient' const createSetMangoDepositoriesRedeemableSoftCapInstruction = ( connection: Connection, uxdProgramId: PublicKey, softCapUiAmount: number, authority: PublicKey, - controllerPda: PublicKey, wallet: SignerWalletAdapter ): TransactionInstruction => { const client = uxdClient(connection, uxdProgramId, wallet) return client.createSetMangoDepositoriesRedeemableSoftCapInstruction( - { pda: controllerPda } as Controller, + { pda: getControllerPda(uxdProgramId) } as Controller, authority, softCapUiAmount, Provider.defaultOptions() diff --git a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts index 40a70271af..2eba0ded67 100644 --- a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts @@ -2,20 +2,19 @@ import { Provider } from '@project-serum/anchor' import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' import { Controller } from '@uxdprotocol/uxd-client' -import { uxdClient } from './uxdClient' +import { getControllerPda, uxdClient } from './uxdClient' const createSetRedeemableGlobalSupplyCapInstruction = ( connection: Connection, uxdProgramId: PublicKey, supplyCapUiAmount: number, authority: PublicKey, - controllerPda: PublicKey, wallet: SignerWalletAdapter ): TransactionInstruction => { const client = uxdClient(connection, uxdProgramId, wallet) return client.createSetRedeemableGlobalSupplyCapInstruction( - { pda: controllerPda } as Controller, + { pda: getControllerPda(uxdProgramId) } as Controller, authority, supplyCapUiAmount, Provider.defaultOptions() diff --git a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts index efefbdfa61..fc81611915 100644 --- a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts @@ -7,6 +7,7 @@ import { uxdClient, initializeMango, instantiateMangoDepository, + getControllerPda, } from './uxdClient' const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( @@ -16,7 +17,6 @@ const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( depositoryMint: PublicKey, insuranceMint: PublicKey, insuranceWithdrawnAmount: number, - controllerPda: PublicKey, wallet: SignerWalletAdapter ): Promise => { const client = uxdClient(connection.current, uxdProgramId, wallet) @@ -34,7 +34,7 @@ const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( return client.createWithdrawInsuranceFromMangoDepositoryInstruction( insuranceWithdrawnAmount, - { pda: controllerPda } as Controller, + { pda: getControllerPda(uxdProgramId) } as Controller, depository, mango, authority, diff --git a/tools/sdk/uxdProtocol/uxdClient.ts b/tools/sdk/uxdProtocol/uxdClient.ts index ab00929b9d..9d050b2afd 100644 --- a/tools/sdk/uxdProtocol/uxdClient.ts +++ b/tools/sdk/uxdProtocol/uxdClient.ts @@ -5,6 +5,7 @@ import { Connection, PublicKey } from '@solana/web3.js' import { Controller, createAndInitializeMango, + findAddrSync, MangoDepository, UXD, } from '@uxdprotocol/uxd-client' @@ -28,6 +29,10 @@ export const initializeMango = async ( return createAndInitializeMango(provider, cluster) } +export const getControllerPda = (uxdProgramId: PublicKey): PublicKey => { + return findAddrSync([Buffer.from('CONTROLLER')], uxdProgramId)[0] +} + // We do not need the mint symbol so it is just set with a placeholder value export const instantiateController = ( uxdProgramId: PublicKey, diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 40a749a9da..c9f43002ec 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -77,14 +77,12 @@ export interface SetRedeemableGlobalSupplyCapForm { governedAccount: GovernedProgramAccount | undefined supplyCap: number programId: string | undefined - controllerPda: string } export interface SetMangoDepositoriesRedeemableSoftCapForm { governedAccount: GovernedProgramAccount | undefined softCap: number programId: string | undefined - controllerPda: string } export interface RegisterMangoDepositoryForm { @@ -92,7 +90,6 @@ export interface RegisterMangoDepositoryForm { collateralMint: string insuranceMint: string programId: string | undefined - controllerPda: string } export interface DepositInsuranceToMangoDepositoryForm { @@ -101,7 +98,6 @@ export interface DepositInsuranceToMangoDepositoryForm { insuranceMint: string insuranceDepositedAmount: number programId: string | undefined - controllerPda: string } export interface WithdrawInsuranceFromMangoDepositoryForm { @@ -110,7 +106,6 @@ export interface WithdrawInsuranceFromMangoDepositoryForm { insuranceMint: string insuranceWithdrawnAmount: number programId: string | undefined - controllerPda: string } export enum UXDIntructions { From c4b093eb29848f6bc003f1756345d73815d28f07 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sat, 18 Dec 2021 23:21:14 +0900 Subject: [PATCH 040/204] select collateral and insurance with select on symbol --- .gitignore | 4 +- .../DepositInsuranceToMangoDepository.tsx | 79 ++++++++++-------- .../instructions/RegisterMangoDepository.tsx | 81 +++++++++++-------- .../WithdrawInsuranceFromMangoDepository.tsx | 80 ++++++++++-------- ...itInsuranceToMangoDepositoryInstruction.ts | 11 ++- ...reateRegisterMangoDepositoryInstruction.ts | 16 +++- ...InsuranceFromMangoDepositoryInstruction.ts | 13 +-- tools/sdk/uxdProtocol/uxdClient.ts | 67 +++++++++++++++ utils/formValidation.tsx | 2 +- utils/uiTypes/proposalCreationTypes.ts | 12 +-- 10 files changed, 243 insertions(+), 122 deletions(-) diff --git a/.gitignore b/.gitignore index 5d520a7b7d..fccb9073ee 100644 --- a/.gitignore +++ b/.gitignore @@ -34,5 +34,5 @@ yarn-error.log* .vercel -/.github/ -.npmrc \ No newline at end of file +.npmrc +.env diff --git a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx index 4a8b917104..e85d89f2e8 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx @@ -19,6 +19,11 @@ import { debounce } from '@utils/debounce' import GovernedAccountSelect from '../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createDepositInsuranceToMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction' +import Select from '@components/inputs/Select' +import { + getDepositoryMintSymbols, + getInsuranceMintSymbols, +} from '@tools/sdk/uxdProtocol/uxdClient' const DepositInsuranceToMangoDepository = ({ index, @@ -43,8 +48,8 @@ const DepositInsuranceToMangoDepository = ({ const [form, setForm] = useState({ governedAccount: undefined, programId: programId?.toString(), - collateralMint: '', - insuranceMint: '', + collateralName: '', + insuranceName: '', insuranceDepositedAmount: 0, }) const [formErrors, setFormErrors] = useState({}) @@ -71,8 +76,8 @@ const DepositInsuranceToMangoDepository = ({ connection, form.governedAccount?.governance.info.governedAccount, form.governedAccount?.governance.pubkey, - new PublicKey(form.collateralMint), - new PublicKey(form.insuranceMint), + form.collateralName, + form.insuranceName, form.insuranceDepositedAmount, wallet ) @@ -93,22 +98,22 @@ const DepositInsuranceToMangoDepository = ({ }, [realmInfo?.programId]) useEffect(() => { - if (form.collateralMint) { + if (form.collateralName) { debounce.debounceFcn(async () => { const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) }) } - }, [form.collateralMint]) + }, [form.collateralName]) useEffect(() => { - if (form.insuranceMint) { + if (form.insuranceName) { debounce.debounceFcn(async () => { const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) }) } - }, [form.insuranceMint]) + }, [form.insuranceName]) useEffect(() => { if (form.insuranceDepositedAmount) { @@ -126,10 +131,10 @@ const DepositInsuranceToMangoDepository = ({ ) }, [form]) const schema = yup.object().shape({ - collateralMint: yup + collateralName: yup .string() - .required('Collateral Mint address is required'), - insuranceMint: yup.string().required('Insurance Mint address is required'), + .required('Collateral Name address is required'), + insuranceName: yup.string().required('Insurance Name address is required'), insuranceDepositedAmount: yup .number() .moreThan(0, 'Insurance Deposited amount should be more than 0') @@ -153,30 +158,38 @@ const DepositInsuranceToMangoDepository = ({ shouldBeGoverned={shouldBeGoverned} governance={governance} > - - handleSetForm({ - value: evt.target.value, - propertyName: 'collateralMint', - }) + - handleSetForm({ - value: evt.target.value, - propertyName: 'insuranceMint', - }) + error={formErrors['collateralName']} + > + {getDepositoryMintSymbols(connection.cluster).map((value, i) => ( + + {value} + + ))} + + + + ({ governedAccount: undefined, programId: programId?.toString(), - collateralMint: '', - insuranceMint: '', + collateralName: '', + insuranceName: '', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -64,6 +68,7 @@ const RegisterMangoDepository = ({ isValid && programId && form.governedAccount?.governance?.info && + form.insuranceName && wallet?.publicKey ) { const createIx = await createRegisterMangoDepositoryInstruction( @@ -71,8 +76,8 @@ const RegisterMangoDepository = ({ form.governedAccount?.governance.info.governedAccount, form.governedAccount?.governance.pubkey, new PublicKey(wallet.publicKey.toBase58()), - new PublicKey(form.collateralMint), - new PublicKey(form.insuranceMint), + form.collateralName, + form.insuranceName, wallet ) serializedInstruction = serializeInstructionToBase64(createIx) @@ -93,22 +98,22 @@ const RegisterMangoDepository = ({ }, [realmInfo?.programId]) useEffect(() => { - if (form.collateralMint) { + if (form.collateralName) { debounce.debounceFcn(async () => { const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) }) } - }, [form.collateralMint]) + }, [form.collateralName]) useEffect(() => { - if (form.insuranceMint) { + if (form.insuranceName) { debounce.debounceFcn(async () => { const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) }) } - }, [form.insuranceMint]) + }, [form.insuranceName]) useEffect(() => { handleSetInstructions( @@ -118,10 +123,8 @@ const RegisterMangoDepository = ({ }, [form]) const schema = yup.object().shape({ - collateralMint: yup - .string() - .required('Collateral Mint address is required'), - insuranceMint: yup.string().required('Insurance Mint address is required'), + collateralName: yup.string().required('Valid Collateral name is required'), + insuranceName: yup.string().required('Valid Insurance name is required'), governedAccount: yup .object() .nullable() @@ -141,30 +144,38 @@ const RegisterMangoDepository = ({ shouldBeGoverned={shouldBeGoverned} governance={governance} > - - handleSetForm({ - value: evt.target.value, - propertyName: 'collateralMint', - }) + + - handleSetForm({ - value: evt.target.value, - propertyName: 'insuranceMint', - }) + error={formErrors['collateralName']} + > + {getDepositoryMintSymbols(connection.cluster).map((value, i) => ( + + {value} + + ))} + + + ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx index 77596e1250..3097db41db 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx @@ -19,6 +19,11 @@ import { debounce } from '@utils/debounce' import GovernedAccountSelect from '../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createWithdrawInsuranceFromMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction' +import Select from '@components/inputs/Select' +import { + getDepositoryMintSymbols, + getInsuranceMintSymbols, +} from '@tools/sdk/uxdProtocol/uxdClient' const WithdrawInsuranceFromMangoDepository = ({ index, @@ -43,8 +48,8 @@ const WithdrawInsuranceFromMangoDepository = ({ const [form, setForm] = useState({ governedAccount: undefined, programId: programId?.toString(), - collateralMint: '', - insuranceMint: '', + collateralName: '', + insuranceName: '', insuranceWithdrawnAmount: 0, }) const [formErrors, setFormErrors] = useState({}) @@ -71,8 +76,8 @@ const WithdrawInsuranceFromMangoDepository = ({ connection, form.governedAccount?.governance.info.governedAccount, form.governedAccount?.governance.pubkey, - new PublicKey(form.collateralMint), - new PublicKey(form.insuranceMint), + form.collateralName, + form.insuranceName, form.insuranceWithdrawnAmount, wallet ) @@ -93,22 +98,22 @@ const WithdrawInsuranceFromMangoDepository = ({ }, [realmInfo?.programId]) useEffect(() => { - if (form.collateralMint) { + if (form.collateralName) { debounce.debounceFcn(async () => { const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) }) } - }, [form.collateralMint]) + }, [form.collateralName]) useEffect(() => { - if (form.insuranceMint) { + if (form.insuranceName) { debounce.debounceFcn(async () => { const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) }) } - }, [form.insuranceMint]) + }, [form.insuranceName]) useEffect(() => { if (form.insuranceWithdrawnAmount) { @@ -127,10 +132,8 @@ const WithdrawInsuranceFromMangoDepository = ({ }, [form]) const schema = yup.object().shape({ - collateralMint: yup - .string() - .required('Collateral Mint address is required'), - insuranceMint: yup.string().required('Insurance Mint address is required'), + collateralName: yup.string().required('Collateral Name is required'), + insuranceName: yup.string().required('Insurance Name is required'), insuranceWithdrawnAmount: yup .number() .moreThan(0, 'Insurance Withdrawn amount should be more than 0') @@ -154,30 +157,39 @@ const WithdrawInsuranceFromMangoDepository = ({ shouldBeGoverned={shouldBeGoverned} governance={governance} > - - handleSetForm({ - value: evt.target.value, - propertyName: 'collateralMint', - }) + + - handleSetForm({ - value: evt.target.value, - propertyName: 'insuranceMint', - }) + error={formErrors['collateralName']} + > + {getDepositoryMintSymbols(connection.cluster).map((value, i) => ( + + {value} + + ))} + + + + => { @@ -26,10 +28,11 @@ const createDepositInsuranceToMangoDepositoryInstruction = async ( connection.cluster, wallet ) + const depository = instantiateMangoDepository( uxdProgramId, - depositoryMint, - insuranceMint + getDepositoryMintKey(connection.cluster, depositoryMintName), + getInsuranceMintKey(connection.cluster, insuranceMintName) ) return client.createDepositInsuranceToMangoDepositoryInstruction( diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index f0cf21beb2..b1313e49a8 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -8,6 +8,8 @@ import { Controller, findATAAddrSync } from '@uxdprotocol/uxd-client' import { ConnectionContext } from 'stores/useWalletStore' import { getControllerPda, + getDepositoryMintKey, + getInsuranceMintKey, initializeMango, instantiateMangoDepository, uxdClient, @@ -18,8 +20,8 @@ const createRegisterMangoDepositoryInstruction = async ( uxdProgramId: PublicKey, authority: PublicKey, payer: PublicKey, - depositoryMint: PublicKey, - insuranceMint: PublicKey, + depositoryMintName: string, + insuranceMintName: string, wallet: SignerWalletAdapter ): Promise => { const mango = await initializeMango( @@ -27,6 +29,16 @@ const createRegisterMangoDepositoryInstruction = async ( connection.cluster, wallet ) + + const depositoryMint = getDepositoryMintKey( + connection.cluster, + depositoryMintName + ) + const insuranceMint = getInsuranceMintKey( + connection.cluster, + insuranceMintName + ) + const depository = instantiateMangoDepository( uxdProgramId, depositoryMint, diff --git a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts index fc81611915..097b14efac 100644 --- a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts @@ -8,28 +8,31 @@ import { initializeMango, instantiateMangoDepository, getControllerPda, + getDepositoryMintKey, + getInsuranceMintKey, } from './uxdClient' const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( connection: ConnectionContext, uxdProgramId: PublicKey, authority: PublicKey, - depositoryMint: PublicKey, - insuranceMint: PublicKey, + depositoryMintName: string, + insuranceMintName: string, insuranceWithdrawnAmount: number, wallet: SignerWalletAdapter ): Promise => { const client = uxdClient(connection.current, uxdProgramId, wallet) - + console.log(depositoryMintName, insuranceMintName) const mango = await initializeMango( connection.current, connection.cluster, wallet ) + const depository = instantiateMangoDepository( uxdProgramId, - depositoryMint, - insuranceMint + getDepositoryMintKey(connection.cluster, depositoryMintName), + getInsuranceMintKey(connection.cluster, insuranceMintName) ) return client.createWithdrawInsuranceFromMangoDepositoryInstruction( diff --git a/tools/sdk/uxdProtocol/uxdClient.ts b/tools/sdk/uxdProtocol/uxdClient.ts index 9d050b2afd..8aa03afe72 100644 --- a/tools/sdk/uxdProtocol/uxdClient.ts +++ b/tools/sdk/uxdProtocol/uxdClient.ts @@ -1,3 +1,4 @@ +import { Cluster } from '@blockworks-foundation/mango-client' import { EndpointTypes } from '@models/types' import { Program, Provider } from '@project-serum/anchor' import Wallet from '@project-serum/sol-wallet-adapter' @@ -8,9 +9,75 @@ import { findAddrSync, MangoDepository, UXD, + UXDHelpers, } from '@uxdprotocol/uxd-client' import uxdIdl from './uxdIdl' +export const DEPOSITORY_MINTS = { + devnet: { + BTC: '3UNBZ6o52WTWwjac2kPUb4FyodhU1vFkRJheu1Sh2TvU', + SOL: 'So11111111111111111111111111111111111111112', + }, + mainnet: { + BTC: '9n4nbM75f5Ui33ZbPYXn59EwSgE8CGsHtAeTH5YFeJ9E', + SOL: 'So11111111111111111111111111111111111111112', + MSOL: 'mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So', + }, +} + +export const INSURANCE_MINTS = { + devnet: { + USDC: '8FRFC6MoGGkMFQwngccyu69VnYbzykGeez7ignHVAFSN', + }, + mainnet: { + USDC: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + }, +} + +export const getDepositoryMintSymbols = (cluster: Cluster): string[] => [ + ...Object.keys(DEPOSITORY_MINTS[cluster]), +] +export const getDepositoryMintKey = ( + cluster: Cluster, + symbol: string +): PublicKey => new PublicKey(DEPOSITORY_MINTS[cluster][symbol]) + +export const getInsuranceMintSymbols = (cluster: Cluster): string[] => [ + ...Object.keys(INSURANCE_MINTS[cluster]), +] +export const getInsuranceMintKey = ( + cluster: Cluster, + symbol: string +): PublicKey => new PublicKey(INSURANCE_MINTS[cluster][symbol]) + +export const isDepositoryRegistered = async ( + connection: Connection, + cluster: Cluster, + uxdProgram: Program, + collateralName: string, + insuranceName: string, + wallet: Wallet +): Promise => { + const uxdHelper = new UXDHelpers() + try { + await uxdHelper.getMangoDepositoryAccount( + new Provider(connection, wallet, Provider.defaultOptions()), + uxdProgram, + instantiateMangoDepository( + uxdProgram.programId, + getDepositoryMintKey(cluster, collateralName), + getInsuranceMintKey(cluster, insuranceName) + ), + Provider.defaultOptions() + ) + + return true + } catch (e) { + console.error(e) + return false + } +} + export const uxdClient = ( connection: Connection, programId: PublicKey, diff --git a/utils/formValidation.tsx b/utils/formValidation.tsx index 42f2ea90d9..1f34ac4e53 100644 --- a/utils/formValidation.tsx +++ b/utils/formValidation.tsx @@ -7,7 +7,7 @@ export interface formValidation { export const isFormValid = async (schema, formValues, abortEarly = false) => { if (!schema) { - throw 'pleas provide schema' + throw 'please provide schema' } const values = new SanitizedObject({ isValid: false, diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index c9f43002ec..4f0dc15a04 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -87,23 +87,23 @@ export interface SetMangoDepositoriesRedeemableSoftCapForm { export interface RegisterMangoDepositoryForm { governedAccount: GovernedProgramAccount | undefined - collateralMint: string - insuranceMint: string + collateralName: string + insuranceName: string programId: string | undefined } export interface DepositInsuranceToMangoDepositoryForm { governedAccount: GovernedProgramAccount | undefined - collateralMint: string - insuranceMint: string + collateralName: string + insuranceName: string insuranceDepositedAmount: number programId: string | undefined } export interface WithdrawInsuranceFromMangoDepositoryForm { governedAccount: GovernedProgramAccount | undefined - collateralMint: string - insuranceMint: string + collateralName: string + insuranceName: string insuranceWithdrawnAmount: number programId: string | undefined } From b7f0a93aee825f78385fb65f7a25353646df8c83 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 19 Dec 2021 00:46:49 +0900 Subject: [PATCH 041/204] fix ConnectionContext import --- .../createDepositInsuranceToMangoDepositoryInstruction.ts | 2 +- .../sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts | 2 +- .../createWithdrawInsuranceFromMangoDepositoryInstruction.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts index 25468dd223..5d35787f87 100644 --- a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts @@ -2,7 +2,7 @@ import { Provider } from '@project-serum/anchor' import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { Controller } from '@uxdprotocol/uxd-client' -import { ConnectionContext } from 'stores/useWalletStore' +import type { ConnectionContext } from 'utils/connection' import { uxdClient, initializeMango, diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index b1313e49a8..cbee1be073 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -5,7 +5,7 @@ import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { TOKEN_PROGRAM_ID } from '@utils/tokens' import { Controller, findATAAddrSync } from '@uxdprotocol/uxd-client' -import { ConnectionContext } from 'stores/useWalletStore' +import type { ConnectionContext } from 'utils/connection' import { getControllerPda, getDepositoryMintKey, diff --git a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts index 097b14efac..349f191ad1 100644 --- a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts @@ -2,7 +2,7 @@ import { Provider } from '@project-serum/anchor' import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { Controller } from '@uxdprotocol/uxd-client' -import { ConnectionContext } from 'stores/useWalletStore' +import type { ConnectionContext } from 'utils/connection' import { uxdClient, initializeMango, From b6bad99164232f7d91c0ea95f6f863e33a303877 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Mon, 20 Dec 2021 11:22:38 +0900 Subject: [PATCH 042/204] fix: yarn types for react-virtualized --- package.json | 3 ++- tsconfig.tsbuildinfo | 2 +- yarn.lock | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0c4af96f4e..79597b8255 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "@types/jest": "^26.0.20", "@types/node": "^14.14.25", "@types/react": "^17.0.1", + "@types/react-virtualized": "^9.21.15", "@typescript-eslint/eslint-plugin": "^5.7.0", "@typescript-eslint/parser": "^5.7.0", "babel-jest": "^26.6.3", @@ -77,8 +78,8 @@ "jest": "^26.6.3", "jest-watch-typeahead": "^0.6.1", "lint-staged": "^10.0.10", - "patch-package": "^6.4.7", "next-router-mock": "^0.6.3", + "patch-package": "^6.4.7", "postcss": "^8.2.12", "postcss-preset-env": "^6.7.0", "postinstall-postinstall": "^2.1.0", diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo index dca38dd485..ee9d8fe62d 100644 --- a/tsconfig.tsbuildinfo +++ b/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"program":{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/globals.global.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/ts3.6/base.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/base.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/prop-types/index.d.ts","./node_modules/@types/scheduler/tracing.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/styled-jsx/index.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/server/image-config.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/@next/env/types/index.d.ts","./node_modules/next/dist/build/webpack/plugins/build-manifest-plugin.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/server/api-utils.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/get-middleware-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/next/dist/shared/lib/router/utils/sorted-routes.d.ts","./node_modules/next/dist/shared/lib/router/utils/is-dynamic.d.ts","./node_modules/next/dist/shared/lib/router/utils/index.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/server/router.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/server/font-utils.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/shared/lib/i18n/normalize-locale-path.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-next-url.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/types/index.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/image-types/global.d.ts","./next-env.d.ts","./@types/buffer-layout.d.ts","./@types/index.d.ts","./node_modules/buffer/index.d.ts","./node_modules/@solana/web3.js/lib/index.d.ts","./@types/types.ts","./models/core/errors.ts","./models/core/accounts.ts","./node_modules/@types/bn.js/index.d.ts","./node_modules/borsh/lib/index.d.ts","./utils/borsh.ts","./models/registry/constants.ts","./node_modules/eventemitter3/index.d.ts","./node_modules/@solana/wallet-adapter-base/lib/errors.d.ts","./node_modules/@solana/wallet-adapter-base/lib/adapter.d.ts","./node_modules/@solana/wallet-adapter-base/lib/poll.d.ts","./node_modules/@solana/wallet-adapter-base/lib/signer.d.ts","./node_modules/@solana/wallet-adapter-base/lib/index.d.ts","./models/core/api.ts","./node_modules/bignumber.js/bignumber.d.ts","./node_modules/moment/ts3.1-typings/moment.d.ts","./models/accounts.ts","./node_modules/zustand/vanilla.d.ts","./node_modules/zustand/index.d.ts","./node_modules/immer/dist/utils/env.d.ts","./node_modules/immer/dist/utils/errors.d.ts","./node_modules/immer/dist/types/types-external.d.ts","./node_modules/immer/dist/types/types-internal.d.ts","./node_modules/immer/dist/utils/common.d.ts","./node_modules/immer/dist/utils/plugins.d.ts","./node_modules/immer/dist/core/scope.d.ts","./node_modules/immer/dist/core/finalize.d.ts","./node_modules/immer/dist/core/proxy.d.ts","./node_modules/immer/dist/core/immerclass.d.ts","./node_modules/immer/dist/core/current.d.ts","./node_modules/immer/dist/internal.d.ts","./node_modules/immer/dist/plugins/es5.d.ts","./node_modules/immer/dist/plugins/patches.d.ts","./node_modules/immer/dist/plugins/mapset.d.ts","./node_modules/immer/dist/plugins/all.d.ts","./node_modules/immer/dist/immer.d.ts","./stores/usenotificationstore.tsx","./utils/notifications.tsx","./utils/send.tsx","./models/instructions.ts","./models/serialisation.ts","./models/withcancelproposal.ts","./actions/cancelproposal.ts","./models/withcastvote.ts","./actions/castvote.ts","./models/withcreateproposal.ts","./models/withaddsignatory.ts","./models/withinsertinstruction.ts","./models/withsignoffproposal.ts","./actions/createproposal.ts","./node_modules/@solana/spl-token/node_modules/buffer/index.d.ts","./node_modules/@solana/spl-token/node_modules/@solana/web3.js/lib/index.d.ts","./node_modules/@solana/spl-token/lib/index.d.ts","./utils/helpers.ts","./components/instructions/programs/bpfupgradeableloader.tsx","./node_modules/@project-serum/anchor/node_modules/@solana/web3.js/lib/index.d.ts","./node_modules/@project-serum/anchor/dist/provider.d.ts","./node_modules/@project-serum/anchor/dist/idl.d.ts","./node_modules/@project-serum/anchor/dist/coder/instruction.d.ts","./node_modules/@project-serum/anchor/dist/coder/accounts.d.ts","./node_modules/@project-serum/anchor/dist/coder/types.d.ts","./node_modules/@project-serum/anchor/dist/program/event.d.ts","./node_modules/@project-serum/anchor/dist/coder/event.d.ts","./node_modules/@project-serum/anchor/dist/coder/state.d.ts","./node_modules/@project-serum/anchor/dist/coder/common.d.ts","./node_modules/@project-serum/anchor/dist/coder/index.d.ts","./node_modules/@project-serum/anchor/dist/workspace.d.ts","./node_modules/@project-serum/anchor/dist/utils/sha256.d.ts","./node_modules/@project-serum/anchor/dist/utils/rpc.d.ts","./node_modules/@project-serum/anchor/dist/program/context.d.ts","./node_modules/@project-serum/anchor/dist/program/common.d.ts","./node_modules/@project-serum/anchor/dist/utils/pubkey.d.ts","./node_modules/@project-serum/anchor/dist/utils/bytes/hex.d.ts","./node_modules/@project-serum/anchor/dist/utils/bytes/utf8.d.ts","./node_modules/@project-serum/anchor/dist/utils/bytes/bs58.d.ts","./node_modules/@project-serum/anchor/dist/utils/bytes/base64.d.ts","./node_modules/@project-serum/anchor/dist/utils/bytes/index.d.ts","./node_modules/@project-serum/anchor/dist/utils/token.d.ts","./node_modules/@project-serum/anchor/dist/utils/index.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/state.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/instruction.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/transaction.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/rpc.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/account.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/simulate.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/index.d.ts","./node_modules/@project-serum/anchor/dist/program/index.d.ts","./node_modules/@project-serum/anchor/dist/index.d.ts","./tools/sdk/units.ts","./models/core/serialisation.ts","./tools/core/script.ts","./models/api.ts","./components/instructions/programs/governance.tsx","./node_modules/@blockworks-foundation/mango-client/lib/src/ids.json","./node_modules/@blockworks-foundation/mango-client/lib/src/mango_logs.json","./node_modules/@project-serum/serum/node_modules/@solana/web3.js/lib/index.d.ts","./node_modules/@project-serum/serum/lib/token-instructions.d.ts","./node_modules/@project-serum/serum/lib/error.d.ts","./node_modules/@project-serum/serum/lib/slab.d.ts","./node_modules/@project-serum/serum/lib/queue.d.ts","./node_modules/@project-serum/serum/lib/market.d.ts","./node_modules/@project-serum/serum/lib/layout.d.ts","./node_modules/@project-serum/serum/lib/instructions.d.ts","./node_modules/@project-serum/serum/lib/fees.d.ts","./node_modules/@project-serum/serum/lib/tokens_and_markets.d.ts","./node_modules/@project-serum/serum/lib/market-proxy/middleware.d.ts","./node_modules/@project-serum/serum/lib/market-proxy/index.d.ts","./node_modules/@project-serum/serum/lib/index.d.ts","./node_modules/@blockworks-foundation/mango-client/node_modules/@solana/web3.js/lib/index.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/fixednum.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/types.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/rootbank.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/mangogroup.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/book.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/perpmarket.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/perpaccount.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/layout.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/mangoaccount.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/perpeventqueue.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/client.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/config.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/instruction.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/token.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/utils.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/index.d.ts","./components/instructions/programs/mango.tsx","./components/instructions/programs/names.ts","./components/instructions/programs/raydium.tsx","./components/instructions/programs/spltoken.tsx","./components/instructions/tools.tsx","./node_modules/axios/index.d.ts","./utils/services/token.tsx","./utils/tokens.tsx","./models/withcreatetokengovernance.ts","./node_modules/@project-serum/common/node_modules/@solana/web3.js/lib/index.d.ts","./node_modules/@project-serum/common/dist/lib/provider.d.ts","./node_modules/@project-serum/common/node_modules/@solana/spl-token/node_modules/buffer/index.d.ts","./node_modules/@project-serum/common/node_modules/@solana/spl-token/lib/index.d.ts","./node_modules/@project-serum/common/dist/lib/token.d.ts","./node_modules/@project-serum/common/dist/lib/simulate-transaction.d.ts","./node_modules/@project-serum/common/node_modules/superstruct/lib/struct-error.d.ts","./node_modules/@project-serum/common/node_modules/superstruct/lib/types.d.ts","./node_modules/@project-serum/common/node_modules/superstruct/lib/struct.d.ts","./node_modules/@project-serum/common/node_modules/superstruct/lib/superstruct.d.ts","./node_modules/@project-serum/common/node_modules/superstruct/lib/index.d.ts","./node_modules/@project-serum/common/dist/lib/connection.d.ts","./node_modules/@project-serum/common/dist/lib/index.d.ts","./models/withcreatespltokenaccount.ts","./actions/createtreasuryaccount.ts","./actions/dryruninstruction.ts","./models/withexecuteinstruction.ts","./actions/executeinstruction.ts","./models/withfinalizevote.ts","./actions/finalizevotes.ts","./models/withflaginstructionerror.ts","./actions/flaginstructionerror.ts","./models/withcreaterealm.ts","./actions/registerrealm.ts","./models/withrelinquishvote.ts","./actions/relinquishvote.ts","./actions/signoffproposal.ts","./models/chat/accounts.ts","./models/chat/instructions.ts","./models/chat/serialisation.ts","./models/chat/withpostchatmessage.ts","./actions/chat/postmessage.ts","./components/members/types.ts","./components/treasuryaccount/types.ts","./components/explorer/tools.ts","./utils/uitypes/proposalcreationtypes.ts","./node_modules/@solana/wallet-adapter-phantom/lib/adapter.d.ts","./node_modules/@solana/wallet-adapter-phantom/lib/index.d.ts","./node_modules/@solana/wallet-adapter-sollet/lib/adapter.d.ts","./node_modules/@solana/wallet-adapter-sollet/lib/index.d.ts","./utils/wallet-adapters/index.ts","./utils/github.ts","./scripts/api.ts","./models/chat/api.ts","./models/enums.ts","./public/realms/devnet.json","./public/realms/mainnet-beta.json","./models/types.ts","./utils/connection.ts","./tools/core/strings.ts","./models/registry/api.ts","./tools/core/pubkey.ts","./stores/usewalletstore.tsx","./node_modules/next/router.d.ts","./models/voteweights.ts","./hooks/userealm.tsx","./hooks/usegovernanceassets.ts","./hooks/useisbeyondtimestamp.ts","./hooks/usehasvotetimeexpired.ts","./hooks/useprevious.ts","./hooks/userealmgovernance.ts","./hooks/usequerycontext.tsx","./hooks/userouterhistory.ts","./models/createsetgovernanceconfig.ts","./models/createsetrealmconfig.ts","./models/errors.ts","./models/withcreateaccountgovernance.ts","./models/withcreatemintgovernance.ts","./models/withcreateprogramgovernance.ts","./models/withdepositgoverningtokens.ts","./models/withremoveinstruction.ts","./models/withsetrealmauthority.ts","./models/withwithdrawgoverningtokens.ts","./pages/api/hello.ts","./scripts/governance-notifier.ts","./node_modules/@types/aria-query/index.d.ts","./node_modules/@testing-library/dom/types/matches.d.ts","./node_modules/@testing-library/dom/types/wait-for.d.ts","./node_modules/@testing-library/dom/types/query-helpers.d.ts","./node_modules/@testing-library/dom/types/queries.d.ts","./node_modules/@testing-library/dom/types/get-queries-for-element.d.ts","./node_modules/pretty-format/build/types.d.ts","./node_modules/pretty-format/build/index.d.ts","./node_modules/@testing-library/dom/types/screen.d.ts","./node_modules/@testing-library/dom/types/wait.d.ts","./node_modules/@testing-library/dom/types/wait-for-dom-change.d.ts","./node_modules/@testing-library/dom/types/wait-for-element.d.ts","./node_modules/@testing-library/dom/types/wait-for-element-to-be-removed.d.ts","./node_modules/@testing-library/dom/types/get-node-text.d.ts","./node_modules/@testing-library/dom/types/events.d.ts","./node_modules/@testing-library/dom/types/pretty-dom.d.ts","./node_modules/@testing-library/dom/types/role-helpers.d.ts","./node_modules/@testing-library/dom/types/config.d.ts","./node_modules/@testing-library/dom/types/suggestions.d.ts","./node_modules/@testing-library/dom/types/index.d.ts","./node_modules/@testing-library/react/types/index.d.ts","./test/testutils.ts","./tools/core/option.ts","./tools/core/resources.ts","./tools/sdk/bpfupgradeableloader/createsetupgradeauthority.ts","./tools/sdk/bpfupgradeableloader/createupgradeinstruction.ts","./node_modules/superstruct/lib/error.d.ts","./node_modules/superstruct/lib/utils.d.ts","./node_modules/superstruct/lib/struct.d.ts","./node_modules/superstruct/lib/structs/coercions.d.ts","./node_modules/superstruct/lib/structs/refinements.d.ts","./node_modules/superstruct/lib/structs/types.d.ts","./node_modules/superstruct/lib/structs/utilities.d.ts","./node_modules/superstruct/lib/index.d.ts","./tools/validators/pubkey.ts","./tools/validators/accounts/token.ts","./tools/validators/accounts/upgradeable-program.ts","./node_modules/yup/lib/reference.d.ts","./node_modules/yup/lib/condition.d.ts","./node_modules/yup/lib/validationerror.d.ts","./node_modules/yup/lib/util/createvalidation.d.ts","./node_modules/yup/lib/util/types.d.ts","./node_modules/yup/lib/util/referenceset.d.ts","./node_modules/yup/lib/schema.d.ts","./node_modules/yup/lib/lazy.d.ts","./node_modules/yup/lib/types.d.ts","./node_modules/yup/lib/locale.d.ts","./node_modules/yup/lib/mixed.d.ts","./node_modules/yup/lib/boolean.d.ts","./node_modules/yup/lib/string.d.ts","./node_modules/yup/lib/number.d.ts","./node_modules/yup/lib/date.d.ts","./node_modules/yup/lib/object.d.ts","./node_modules/yup/lib/array.d.ts","./node_modules/yup/lib/util/reach.d.ts","./node_modules/yup/lib/util/isschema.d.ts","./node_modules/yup/lib/setlocale.d.ts","./node_modules/yup/lib/index.d.ts","./utils/validations.tsx","./utils/atatools.tsx","./utils/formvalidation.tsx","./utils/instructiontools.ts","./node_modules/@popperjs/core/lib/enums.d.ts","./node_modules/@popperjs/core/lib/modifiers/popperoffsets.d.ts","./node_modules/@popperjs/core/lib/modifiers/flip.d.ts","./node_modules/@popperjs/core/lib/modifiers/hide.d.ts","./node_modules/@popperjs/core/lib/modifiers/offset.d.ts","./node_modules/@popperjs/core/lib/modifiers/eventlisteners.d.ts","./node_modules/@popperjs/core/lib/modifiers/computestyles.d.ts","./node_modules/@popperjs/core/lib/modifiers/arrow.d.ts","./node_modules/@popperjs/core/lib/modifiers/preventoverflow.d.ts","./node_modules/@popperjs/core/lib/modifiers/applystyles.d.ts","./node_modules/@popperjs/core/lib/types.d.ts","./node_modules/@popperjs/core/lib/modifiers/index.d.ts","./node_modules/@popperjs/core/lib/utils/detectoverflow.d.ts","./node_modules/@popperjs/core/lib/createpopper.d.ts","./node_modules/@popperjs/core/lib/popper-lite.d.ts","./node_modules/@popperjs/core/lib/popper.d.ts","./node_modules/@popperjs/core/lib/index.d.ts","./node_modules/@popperjs/core/index.d.ts","./node_modules/tippy.js/index.d.ts","./node_modules/@tippyjs/react/index.d.ts","./components/tooltip.tsx","./components/approvalquorum.tsx","./components/loading.tsx","./components/button.tsx","./node_modules/@heroicons/react/outline/academiccapicon.d.ts","./node_modules/@heroicons/react/outline/adjustmentsicon.d.ts","./node_modules/@heroicons/react/outline/annotationicon.d.ts","./node_modules/@heroicons/react/outline/archiveicon.d.ts","./node_modules/@heroicons/react/outline/arrowcircledownicon.d.ts","./node_modules/@heroicons/react/outline/arrowcirclelefticon.d.ts","./node_modules/@heroicons/react/outline/arrowcirclerighticon.d.ts","./node_modules/@heroicons/react/outline/arrowcircleupicon.d.ts","./node_modules/@heroicons/react/outline/arrowdownicon.d.ts","./node_modules/@heroicons/react/outline/arrowlefticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowdownicon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowlefticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowrighticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowupicon.d.ts","./node_modules/@heroicons/react/outline/arrowrighticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmdownicon.d.ts","./node_modules/@heroicons/react/outline/arrowsmlefticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmrighticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmupicon.d.ts","./node_modules/@heroicons/react/outline/arrowupicon.d.ts","./node_modules/@heroicons/react/outline/arrowsexpandicon.d.ts","./node_modules/@heroicons/react/outline/atsymbolicon.d.ts","./node_modules/@heroicons/react/outline/backspaceicon.d.ts","./node_modules/@heroicons/react/outline/badgecheckicon.d.ts","./node_modules/@heroicons/react/outline/banicon.d.ts","./node_modules/@heroicons/react/outline/beakericon.d.ts","./node_modules/@heroicons/react/outline/bellicon.d.ts","./node_modules/@heroicons/react/outline/bookopenicon.d.ts","./node_modules/@heroicons/react/outline/bookmarkalticon.d.ts","./node_modules/@heroicons/react/outline/bookmarkicon.d.ts","./node_modules/@heroicons/react/outline/briefcaseicon.d.ts","./node_modules/@heroicons/react/outline/cakeicon.d.ts","./node_modules/@heroicons/react/outline/calculatoricon.d.ts","./node_modules/@heroicons/react/outline/calendaricon.d.ts","./node_modules/@heroicons/react/outline/cameraicon.d.ts","./node_modules/@heroicons/react/outline/cashicon.d.ts","./node_modules/@heroicons/react/outline/chartbaricon.d.ts","./node_modules/@heroicons/react/outline/chartpieicon.d.ts","./node_modules/@heroicons/react/outline/chartsquarebaricon.d.ts","./node_modules/@heroicons/react/outline/chatalt2icon.d.ts","./node_modules/@heroicons/react/outline/chatalticon.d.ts","./node_modules/@heroicons/react/outline/chaticon.d.ts","./node_modules/@heroicons/react/outline/checkcircleicon.d.ts","./node_modules/@heroicons/react/outline/checkicon.d.ts","./node_modules/@heroicons/react/outline/chevrondoubledownicon.d.ts","./node_modules/@heroicons/react/outline/chevrondoublelefticon.d.ts","./node_modules/@heroicons/react/outline/chevrondoublerighticon.d.ts","./node_modules/@heroicons/react/outline/chevrondoubleupicon.d.ts","./node_modules/@heroicons/react/outline/chevrondownicon.d.ts","./node_modules/@heroicons/react/outline/chevronlefticon.d.ts","./node_modules/@heroicons/react/outline/chevronrighticon.d.ts","./node_modules/@heroicons/react/outline/chevronupicon.d.ts","./node_modules/@heroicons/react/outline/chipicon.d.ts","./node_modules/@heroicons/react/outline/clipboardcheckicon.d.ts","./node_modules/@heroicons/react/outline/clipboardcopyicon.d.ts","./node_modules/@heroicons/react/outline/clipboardlisticon.d.ts","./node_modules/@heroicons/react/outline/clipboardicon.d.ts","./node_modules/@heroicons/react/outline/clockicon.d.ts","./node_modules/@heroicons/react/outline/clouddownloadicon.d.ts","./node_modules/@heroicons/react/outline/clouduploadicon.d.ts","./node_modules/@heroicons/react/outline/cloudicon.d.ts","./node_modules/@heroicons/react/outline/codeicon.d.ts","./node_modules/@heroicons/react/outline/cogicon.d.ts","./node_modules/@heroicons/react/outline/collectionicon.d.ts","./node_modules/@heroicons/react/outline/colorswatchicon.d.ts","./node_modules/@heroicons/react/outline/creditcardicon.d.ts","./node_modules/@heroicons/react/outline/cubetransparenticon.d.ts","./node_modules/@heroicons/react/outline/cubeicon.d.ts","./node_modules/@heroicons/react/outline/currencybangladeshiicon.d.ts","./node_modules/@heroicons/react/outline/currencydollaricon.d.ts","./node_modules/@heroicons/react/outline/currencyeuroicon.d.ts","./node_modules/@heroicons/react/outline/currencypoundicon.d.ts","./node_modules/@heroicons/react/outline/currencyrupeeicon.d.ts","./node_modules/@heroicons/react/outline/currencyyenicon.d.ts","./node_modules/@heroicons/react/outline/cursorclickicon.d.ts","./node_modules/@heroicons/react/outline/databaseicon.d.ts","./node_modules/@heroicons/react/outline/desktopcomputericon.d.ts","./node_modules/@heroicons/react/outline/devicemobileicon.d.ts","./node_modules/@heroicons/react/outline/devicetableticon.d.ts","./node_modules/@heroicons/react/outline/documentaddicon.d.ts","./node_modules/@heroicons/react/outline/documentdownloadicon.d.ts","./node_modules/@heroicons/react/outline/documentduplicateicon.d.ts","./node_modules/@heroicons/react/outline/documentremoveicon.d.ts","./node_modules/@heroicons/react/outline/documentreporticon.d.ts","./node_modules/@heroicons/react/outline/documentsearchicon.d.ts","./node_modules/@heroicons/react/outline/documenttexticon.d.ts","./node_modules/@heroicons/react/outline/documenticon.d.ts","./node_modules/@heroicons/react/outline/dotscirclehorizontalicon.d.ts","./node_modules/@heroicons/react/outline/dotshorizontalicon.d.ts","./node_modules/@heroicons/react/outline/dotsverticalicon.d.ts","./node_modules/@heroicons/react/outline/downloadicon.d.ts","./node_modules/@heroicons/react/outline/duplicateicon.d.ts","./node_modules/@heroicons/react/outline/emojihappyicon.d.ts","./node_modules/@heroicons/react/outline/emojisadicon.d.ts","./node_modules/@heroicons/react/outline/exclamationcircleicon.d.ts","./node_modules/@heroicons/react/outline/exclamationicon.d.ts","./node_modules/@heroicons/react/outline/externallinkicon.d.ts","./node_modules/@heroicons/react/outline/eyeofficon.d.ts","./node_modules/@heroicons/react/outline/eyeicon.d.ts","./node_modules/@heroicons/react/outline/fastforwardicon.d.ts","./node_modules/@heroicons/react/outline/filmicon.d.ts","./node_modules/@heroicons/react/outline/filtericon.d.ts","./node_modules/@heroicons/react/outline/fingerprinticon.d.ts","./node_modules/@heroicons/react/outline/fireicon.d.ts","./node_modules/@heroicons/react/outline/flagicon.d.ts","./node_modules/@heroicons/react/outline/folderaddicon.d.ts","./node_modules/@heroicons/react/outline/folderdownloadicon.d.ts","./node_modules/@heroicons/react/outline/folderopenicon.d.ts","./node_modules/@heroicons/react/outline/folderremoveicon.d.ts","./node_modules/@heroicons/react/outline/foldericon.d.ts","./node_modules/@heroicons/react/outline/gifticon.d.ts","./node_modules/@heroicons/react/outline/globealticon.d.ts","./node_modules/@heroicons/react/outline/globeicon.d.ts","./node_modules/@heroicons/react/outline/handicon.d.ts","./node_modules/@heroicons/react/outline/hashtagicon.d.ts","./node_modules/@heroicons/react/outline/hearticon.d.ts","./node_modules/@heroicons/react/outline/homeicon.d.ts","./node_modules/@heroicons/react/outline/identificationicon.d.ts","./node_modules/@heroicons/react/outline/inboxinicon.d.ts","./node_modules/@heroicons/react/outline/inboxicon.d.ts","./node_modules/@heroicons/react/outline/informationcircleicon.d.ts","./node_modules/@heroicons/react/outline/keyicon.d.ts","./node_modules/@heroicons/react/outline/libraryicon.d.ts","./node_modules/@heroicons/react/outline/lightbulbicon.d.ts","./node_modules/@heroicons/react/outline/lightningbolticon.d.ts","./node_modules/@heroicons/react/outline/linkicon.d.ts","./node_modules/@heroicons/react/outline/locationmarkericon.d.ts","./node_modules/@heroicons/react/outline/lockclosedicon.d.ts","./node_modules/@heroicons/react/outline/lockopenicon.d.ts","./node_modules/@heroicons/react/outline/loginicon.d.ts","./node_modules/@heroicons/react/outline/logouticon.d.ts","./node_modules/@heroicons/react/outline/mailopenicon.d.ts","./node_modules/@heroicons/react/outline/mailicon.d.ts","./node_modules/@heroicons/react/outline/mapicon.d.ts","./node_modules/@heroicons/react/outline/menualt1icon.d.ts","./node_modules/@heroicons/react/outline/menualt2icon.d.ts","./node_modules/@heroicons/react/outline/menualt3icon.d.ts","./node_modules/@heroicons/react/outline/menualt4icon.d.ts","./node_modules/@heroicons/react/outline/menuicon.d.ts","./node_modules/@heroicons/react/outline/microphoneicon.d.ts","./node_modules/@heroicons/react/outline/minuscircleicon.d.ts","./node_modules/@heroicons/react/outline/minussmicon.d.ts","./node_modules/@heroicons/react/outline/minusicon.d.ts","./node_modules/@heroicons/react/outline/moonicon.d.ts","./node_modules/@heroicons/react/outline/musicnoteicon.d.ts","./node_modules/@heroicons/react/outline/newspapericon.d.ts","./node_modules/@heroicons/react/outline/officebuildingicon.d.ts","./node_modules/@heroicons/react/outline/paperairplaneicon.d.ts","./node_modules/@heroicons/react/outline/paperclipicon.d.ts","./node_modules/@heroicons/react/outline/pauseicon.d.ts","./node_modules/@heroicons/react/outline/pencilalticon.d.ts","./node_modules/@heroicons/react/outline/pencilicon.d.ts","./node_modules/@heroicons/react/outline/phoneincomingicon.d.ts","./node_modules/@heroicons/react/outline/phonemissedcallicon.d.ts","./node_modules/@heroicons/react/outline/phoneoutgoingicon.d.ts","./node_modules/@heroicons/react/outline/phoneicon.d.ts","./node_modules/@heroicons/react/outline/photographicon.d.ts","./node_modules/@heroicons/react/outline/playicon.d.ts","./node_modules/@heroicons/react/outline/pluscircleicon.d.ts","./node_modules/@heroicons/react/outline/plussmicon.d.ts","./node_modules/@heroicons/react/outline/plusicon.d.ts","./node_modules/@heroicons/react/outline/presentationchartbaricon.d.ts","./node_modules/@heroicons/react/outline/presentationchartlineicon.d.ts","./node_modules/@heroicons/react/outline/printericon.d.ts","./node_modules/@heroicons/react/outline/puzzleicon.d.ts","./node_modules/@heroicons/react/outline/qrcodeicon.d.ts","./node_modules/@heroicons/react/outline/questionmarkcircleicon.d.ts","./node_modules/@heroicons/react/outline/receiptrefundicon.d.ts","./node_modules/@heroicons/react/outline/receipttaxicon.d.ts","./node_modules/@heroicons/react/outline/refreshicon.d.ts","./node_modules/@heroicons/react/outline/replyicon.d.ts","./node_modules/@heroicons/react/outline/rewindicon.d.ts","./node_modules/@heroicons/react/outline/rssicon.d.ts","./node_modules/@heroicons/react/outline/saveasicon.d.ts","./node_modules/@heroicons/react/outline/saveicon.d.ts","./node_modules/@heroicons/react/outline/scaleicon.d.ts","./node_modules/@heroicons/react/outline/scissorsicon.d.ts","./node_modules/@heroicons/react/outline/searchcircleicon.d.ts","./node_modules/@heroicons/react/outline/searchicon.d.ts","./node_modules/@heroicons/react/outline/selectoricon.d.ts","./node_modules/@heroicons/react/outline/servericon.d.ts","./node_modules/@heroicons/react/outline/shareicon.d.ts","./node_modules/@heroicons/react/outline/shieldcheckicon.d.ts","./node_modules/@heroicons/react/outline/shieldexclamationicon.d.ts","./node_modules/@heroicons/react/outline/shoppingbagicon.d.ts","./node_modules/@heroicons/react/outline/shoppingcarticon.d.ts","./node_modules/@heroicons/react/outline/sortascendingicon.d.ts","./node_modules/@heroicons/react/outline/sortdescendingicon.d.ts","./node_modules/@heroicons/react/outline/sparklesicon.d.ts","./node_modules/@heroicons/react/outline/speakerphoneicon.d.ts","./node_modules/@heroicons/react/outline/staricon.d.ts","./node_modules/@heroicons/react/outline/statusofflineicon.d.ts","./node_modules/@heroicons/react/outline/statusonlineicon.d.ts","./node_modules/@heroicons/react/outline/stopicon.d.ts","./node_modules/@heroicons/react/outline/sunicon.d.ts","./node_modules/@heroicons/react/outline/supporticon.d.ts","./node_modules/@heroicons/react/outline/switchhorizontalicon.d.ts","./node_modules/@heroicons/react/outline/switchverticalicon.d.ts","./node_modules/@heroicons/react/outline/tableicon.d.ts","./node_modules/@heroicons/react/outline/tagicon.d.ts","./node_modules/@heroicons/react/outline/templateicon.d.ts","./node_modules/@heroicons/react/outline/terminalicon.d.ts","./node_modules/@heroicons/react/outline/thumbdownicon.d.ts","./node_modules/@heroicons/react/outline/thumbupicon.d.ts","./node_modules/@heroicons/react/outline/ticketicon.d.ts","./node_modules/@heroicons/react/outline/translateicon.d.ts","./node_modules/@heroicons/react/outline/trashicon.d.ts","./node_modules/@heroicons/react/outline/trendingdownicon.d.ts","./node_modules/@heroicons/react/outline/trendingupicon.d.ts","./node_modules/@heroicons/react/outline/truckicon.d.ts","./node_modules/@heroicons/react/outline/uploadicon.d.ts","./node_modules/@heroicons/react/outline/useraddicon.d.ts","./node_modules/@heroicons/react/outline/usercircleicon.d.ts","./node_modules/@heroicons/react/outline/usergroupicon.d.ts","./node_modules/@heroicons/react/outline/userremoveicon.d.ts","./node_modules/@heroicons/react/outline/usericon.d.ts","./node_modules/@heroicons/react/outline/usersicon.d.ts","./node_modules/@heroicons/react/outline/variableicon.d.ts","./node_modules/@heroicons/react/outline/videocameraicon.d.ts","./node_modules/@heroicons/react/outline/viewboardsicon.d.ts","./node_modules/@heroicons/react/outline/viewgridaddicon.d.ts","./node_modules/@heroicons/react/outline/viewgridicon.d.ts","./node_modules/@heroicons/react/outline/viewlisticon.d.ts","./node_modules/@heroicons/react/outline/volumeofficon.d.ts","./node_modules/@heroicons/react/outline/volumeupicon.d.ts","./node_modules/@heroicons/react/outline/wifiicon.d.ts","./node_modules/@heroicons/react/outline/xcircleicon.d.ts","./node_modules/@heroicons/react/outline/xicon.d.ts","./node_modules/@heroicons/react/outline/zoominicon.d.ts","./node_modules/@heroicons/react/outline/zoomouticon.d.ts","./node_modules/@heroicons/react/outline/index.d.ts","./components/modal.tsx","./hooks/useproposal.tsx","./components/cancelproposalmodal.tsx","./node_modules/@headlessui/react/dist/types.d.ts","./node_modules/@headlessui/react/dist/utils/render.d.ts","./node_modules/@headlessui/react/dist/components/description/description.d.ts","./node_modules/@headlessui/react/dist/components/dialog/dialog.d.ts","./node_modules/@headlessui/react/dist/components/disclosure/disclosure.d.ts","./node_modules/@headlessui/react/dist/components/focus-trap/focus-trap.d.ts","./node_modules/@headlessui/react/dist/components/listbox/listbox.d.ts","./node_modules/@headlessui/react/dist/components/menu/menu.d.ts","./node_modules/@headlessui/react/dist/components/popover/popover.d.ts","./node_modules/@headlessui/react/dist/components/portal/portal.d.ts","./node_modules/@headlessui/react/dist/components/label/label.d.ts","./node_modules/@headlessui/react/dist/components/radio-group/radio-group.d.ts","./node_modules/@headlessui/react/dist/components/switch/switch.d.ts","./node_modules/@headlessui/react/dist/components/transitions/transition.d.ts","./node_modules/@headlessui/react/dist/index.d.ts","./node_modules/@heroicons/react/solid/academiccapicon.d.ts","./node_modules/@heroicons/react/solid/adjustmentsicon.d.ts","./node_modules/@heroicons/react/solid/annotationicon.d.ts","./node_modules/@heroicons/react/solid/archiveicon.d.ts","./node_modules/@heroicons/react/solid/arrowcircledownicon.d.ts","./node_modules/@heroicons/react/solid/arrowcirclelefticon.d.ts","./node_modules/@heroicons/react/solid/arrowcirclerighticon.d.ts","./node_modules/@heroicons/react/solid/arrowcircleupicon.d.ts","./node_modules/@heroicons/react/solid/arrowdownicon.d.ts","./node_modules/@heroicons/react/solid/arrowlefticon.d.ts","./node_modules/@heroicons/react/solid/arrownarrowdownicon.d.ts","./node_modules/@heroicons/react/solid/arrownarrowlefticon.d.ts","./node_modules/@heroicons/react/solid/arrownarrowrighticon.d.ts","./node_modules/@heroicons/react/solid/arrownarrowupicon.d.ts","./node_modules/@heroicons/react/solid/arrowrighticon.d.ts","./node_modules/@heroicons/react/solid/arrowsmdownicon.d.ts","./node_modules/@heroicons/react/solid/arrowsmlefticon.d.ts","./node_modules/@heroicons/react/solid/arrowsmrighticon.d.ts","./node_modules/@heroicons/react/solid/arrowsmupicon.d.ts","./node_modules/@heroicons/react/solid/arrowupicon.d.ts","./node_modules/@heroicons/react/solid/arrowsexpandicon.d.ts","./node_modules/@heroicons/react/solid/atsymbolicon.d.ts","./node_modules/@heroicons/react/solid/backspaceicon.d.ts","./node_modules/@heroicons/react/solid/badgecheckicon.d.ts","./node_modules/@heroicons/react/solid/banicon.d.ts","./node_modules/@heroicons/react/solid/beakericon.d.ts","./node_modules/@heroicons/react/solid/bellicon.d.ts","./node_modules/@heroicons/react/solid/bookopenicon.d.ts","./node_modules/@heroicons/react/solid/bookmarkalticon.d.ts","./node_modules/@heroicons/react/solid/bookmarkicon.d.ts","./node_modules/@heroicons/react/solid/briefcaseicon.d.ts","./node_modules/@heroicons/react/solid/cakeicon.d.ts","./node_modules/@heroicons/react/solid/calculatoricon.d.ts","./node_modules/@heroicons/react/solid/calendaricon.d.ts","./node_modules/@heroicons/react/solid/cameraicon.d.ts","./node_modules/@heroicons/react/solid/cashicon.d.ts","./node_modules/@heroicons/react/solid/chartbaricon.d.ts","./node_modules/@heroicons/react/solid/chartpieicon.d.ts","./node_modules/@heroicons/react/solid/chartsquarebaricon.d.ts","./node_modules/@heroicons/react/solid/chatalt2icon.d.ts","./node_modules/@heroicons/react/solid/chatalticon.d.ts","./node_modules/@heroicons/react/solid/chaticon.d.ts","./node_modules/@heroicons/react/solid/checkcircleicon.d.ts","./node_modules/@heroicons/react/solid/checkicon.d.ts","./node_modules/@heroicons/react/solid/chevrondoubledownicon.d.ts","./node_modules/@heroicons/react/solid/chevrondoublelefticon.d.ts","./node_modules/@heroicons/react/solid/chevrondoublerighticon.d.ts","./node_modules/@heroicons/react/solid/chevrondoubleupicon.d.ts","./node_modules/@heroicons/react/solid/chevrondownicon.d.ts","./node_modules/@heroicons/react/solid/chevronlefticon.d.ts","./node_modules/@heroicons/react/solid/chevronrighticon.d.ts","./node_modules/@heroicons/react/solid/chevronupicon.d.ts","./node_modules/@heroicons/react/solid/chipicon.d.ts","./node_modules/@heroicons/react/solid/clipboardcheckicon.d.ts","./node_modules/@heroicons/react/solid/clipboardcopyicon.d.ts","./node_modules/@heroicons/react/solid/clipboardlisticon.d.ts","./node_modules/@heroicons/react/solid/clipboardicon.d.ts","./node_modules/@heroicons/react/solid/clockicon.d.ts","./node_modules/@heroicons/react/solid/clouddownloadicon.d.ts","./node_modules/@heroicons/react/solid/clouduploadicon.d.ts","./node_modules/@heroicons/react/solid/cloudicon.d.ts","./node_modules/@heroicons/react/solid/codeicon.d.ts","./node_modules/@heroicons/react/solid/cogicon.d.ts","./node_modules/@heroicons/react/solid/collectionicon.d.ts","./node_modules/@heroicons/react/solid/colorswatchicon.d.ts","./node_modules/@heroicons/react/solid/creditcardicon.d.ts","./node_modules/@heroicons/react/solid/cubetransparenticon.d.ts","./node_modules/@heroicons/react/solid/cubeicon.d.ts","./node_modules/@heroicons/react/solid/currencybangladeshiicon.d.ts","./node_modules/@heroicons/react/solid/currencydollaricon.d.ts","./node_modules/@heroicons/react/solid/currencyeuroicon.d.ts","./node_modules/@heroicons/react/solid/currencypoundicon.d.ts","./node_modules/@heroicons/react/solid/currencyrupeeicon.d.ts","./node_modules/@heroicons/react/solid/currencyyenicon.d.ts","./node_modules/@heroicons/react/solid/cursorclickicon.d.ts","./node_modules/@heroicons/react/solid/databaseicon.d.ts","./node_modules/@heroicons/react/solid/desktopcomputericon.d.ts","./node_modules/@heroicons/react/solid/devicemobileicon.d.ts","./node_modules/@heroicons/react/solid/devicetableticon.d.ts","./node_modules/@heroicons/react/solid/documentaddicon.d.ts","./node_modules/@heroicons/react/solid/documentdownloadicon.d.ts","./node_modules/@heroicons/react/solid/documentduplicateicon.d.ts","./node_modules/@heroicons/react/solid/documentremoveicon.d.ts","./node_modules/@heroicons/react/solid/documentreporticon.d.ts","./node_modules/@heroicons/react/solid/documentsearchicon.d.ts","./node_modules/@heroicons/react/solid/documenttexticon.d.ts","./node_modules/@heroicons/react/solid/documenticon.d.ts","./node_modules/@heroicons/react/solid/dotscirclehorizontalicon.d.ts","./node_modules/@heroicons/react/solid/dotshorizontalicon.d.ts","./node_modules/@heroicons/react/solid/dotsverticalicon.d.ts","./node_modules/@heroicons/react/solid/downloadicon.d.ts","./node_modules/@heroicons/react/solid/duplicateicon.d.ts","./node_modules/@heroicons/react/solid/emojihappyicon.d.ts","./node_modules/@heroicons/react/solid/emojisadicon.d.ts","./node_modules/@heroicons/react/solid/exclamationcircleicon.d.ts","./node_modules/@heroicons/react/solid/exclamationicon.d.ts","./node_modules/@heroicons/react/solid/externallinkicon.d.ts","./node_modules/@heroicons/react/solid/eyeofficon.d.ts","./node_modules/@heroicons/react/solid/eyeicon.d.ts","./node_modules/@heroicons/react/solid/fastforwardicon.d.ts","./node_modules/@heroicons/react/solid/filmicon.d.ts","./node_modules/@heroicons/react/solid/filtericon.d.ts","./node_modules/@heroicons/react/solid/fingerprinticon.d.ts","./node_modules/@heroicons/react/solid/fireicon.d.ts","./node_modules/@heroicons/react/solid/flagicon.d.ts","./node_modules/@heroicons/react/solid/folderaddicon.d.ts","./node_modules/@heroicons/react/solid/folderdownloadicon.d.ts","./node_modules/@heroicons/react/solid/folderopenicon.d.ts","./node_modules/@heroicons/react/solid/folderremoveicon.d.ts","./node_modules/@heroicons/react/solid/foldericon.d.ts","./node_modules/@heroicons/react/solid/gifticon.d.ts","./node_modules/@heroicons/react/solid/globealticon.d.ts","./node_modules/@heroicons/react/solid/globeicon.d.ts","./node_modules/@heroicons/react/solid/handicon.d.ts","./node_modules/@heroicons/react/solid/hashtagicon.d.ts","./node_modules/@heroicons/react/solid/hearticon.d.ts","./node_modules/@heroicons/react/solid/homeicon.d.ts","./node_modules/@heroicons/react/solid/identificationicon.d.ts","./node_modules/@heroicons/react/solid/inboxinicon.d.ts","./node_modules/@heroicons/react/solid/inboxicon.d.ts","./node_modules/@heroicons/react/solid/informationcircleicon.d.ts","./node_modules/@heroicons/react/solid/keyicon.d.ts","./node_modules/@heroicons/react/solid/libraryicon.d.ts","./node_modules/@heroicons/react/solid/lightbulbicon.d.ts","./node_modules/@heroicons/react/solid/lightningbolticon.d.ts","./node_modules/@heroicons/react/solid/linkicon.d.ts","./node_modules/@heroicons/react/solid/locationmarkericon.d.ts","./node_modules/@heroicons/react/solid/lockclosedicon.d.ts","./node_modules/@heroicons/react/solid/lockopenicon.d.ts","./node_modules/@heroicons/react/solid/loginicon.d.ts","./node_modules/@heroicons/react/solid/logouticon.d.ts","./node_modules/@heroicons/react/solid/mailopenicon.d.ts","./node_modules/@heroicons/react/solid/mailicon.d.ts","./node_modules/@heroicons/react/solid/mapicon.d.ts","./node_modules/@heroicons/react/solid/menualt1icon.d.ts","./node_modules/@heroicons/react/solid/menualt2icon.d.ts","./node_modules/@heroicons/react/solid/menualt3icon.d.ts","./node_modules/@heroicons/react/solid/menualt4icon.d.ts","./node_modules/@heroicons/react/solid/menuicon.d.ts","./node_modules/@heroicons/react/solid/microphoneicon.d.ts","./node_modules/@heroicons/react/solid/minuscircleicon.d.ts","./node_modules/@heroicons/react/solid/minussmicon.d.ts","./node_modules/@heroicons/react/solid/minusicon.d.ts","./node_modules/@heroicons/react/solid/moonicon.d.ts","./node_modules/@heroicons/react/solid/musicnoteicon.d.ts","./node_modules/@heroicons/react/solid/newspapericon.d.ts","./node_modules/@heroicons/react/solid/officebuildingicon.d.ts","./node_modules/@heroicons/react/solid/paperairplaneicon.d.ts","./node_modules/@heroicons/react/solid/paperclipicon.d.ts","./node_modules/@heroicons/react/solid/pauseicon.d.ts","./node_modules/@heroicons/react/solid/pencilalticon.d.ts","./node_modules/@heroicons/react/solid/pencilicon.d.ts","./node_modules/@heroicons/react/solid/phoneincomingicon.d.ts","./node_modules/@heroicons/react/solid/phonemissedcallicon.d.ts","./node_modules/@heroicons/react/solid/phoneoutgoingicon.d.ts","./node_modules/@heroicons/react/solid/phoneicon.d.ts","./node_modules/@heroicons/react/solid/photographicon.d.ts","./node_modules/@heroicons/react/solid/playicon.d.ts","./node_modules/@heroicons/react/solid/pluscircleicon.d.ts","./node_modules/@heroicons/react/solid/plussmicon.d.ts","./node_modules/@heroicons/react/solid/plusicon.d.ts","./node_modules/@heroicons/react/solid/presentationchartbaricon.d.ts","./node_modules/@heroicons/react/solid/presentationchartlineicon.d.ts","./node_modules/@heroicons/react/solid/printericon.d.ts","./node_modules/@heroicons/react/solid/puzzleicon.d.ts","./node_modules/@heroicons/react/solid/qrcodeicon.d.ts","./node_modules/@heroicons/react/solid/questionmarkcircleicon.d.ts","./node_modules/@heroicons/react/solid/receiptrefundicon.d.ts","./node_modules/@heroicons/react/solid/receipttaxicon.d.ts","./node_modules/@heroicons/react/solid/refreshicon.d.ts","./node_modules/@heroicons/react/solid/replyicon.d.ts","./node_modules/@heroicons/react/solid/rewindicon.d.ts","./node_modules/@heroicons/react/solid/rssicon.d.ts","./node_modules/@heroicons/react/solid/saveasicon.d.ts","./node_modules/@heroicons/react/solid/saveicon.d.ts","./node_modules/@heroicons/react/solid/scaleicon.d.ts","./node_modules/@heroicons/react/solid/scissorsicon.d.ts","./node_modules/@heroicons/react/solid/searchcircleicon.d.ts","./node_modules/@heroicons/react/solid/searchicon.d.ts","./node_modules/@heroicons/react/solid/selectoricon.d.ts","./node_modules/@heroicons/react/solid/servericon.d.ts","./node_modules/@heroicons/react/solid/shareicon.d.ts","./node_modules/@heroicons/react/solid/shieldcheckicon.d.ts","./node_modules/@heroicons/react/solid/shieldexclamationicon.d.ts","./node_modules/@heroicons/react/solid/shoppingbagicon.d.ts","./node_modules/@heroicons/react/solid/shoppingcarticon.d.ts","./node_modules/@heroicons/react/solid/sortascendingicon.d.ts","./node_modules/@heroicons/react/solid/sortdescendingicon.d.ts","./node_modules/@heroicons/react/solid/sparklesicon.d.ts","./node_modules/@heroicons/react/solid/speakerphoneicon.d.ts","./node_modules/@heroicons/react/solid/staricon.d.ts","./node_modules/@heroicons/react/solid/statusofflineicon.d.ts","./node_modules/@heroicons/react/solid/statusonlineicon.d.ts","./node_modules/@heroicons/react/solid/stopicon.d.ts","./node_modules/@heroicons/react/solid/sunicon.d.ts","./node_modules/@heroicons/react/solid/supporticon.d.ts","./node_modules/@heroicons/react/solid/switchhorizontalicon.d.ts","./node_modules/@heroicons/react/solid/switchverticalicon.d.ts","./node_modules/@heroicons/react/solid/tableicon.d.ts","./node_modules/@heroicons/react/solid/tagicon.d.ts","./node_modules/@heroicons/react/solid/templateicon.d.ts","./node_modules/@heroicons/react/solid/terminalicon.d.ts","./node_modules/@heroicons/react/solid/thumbdownicon.d.ts","./node_modules/@heroicons/react/solid/thumbupicon.d.ts","./node_modules/@heroicons/react/solid/ticketicon.d.ts","./node_modules/@heroicons/react/solid/translateicon.d.ts","./node_modules/@heroicons/react/solid/trashicon.d.ts","./node_modules/@heroicons/react/solid/trendingdownicon.d.ts","./node_modules/@heroicons/react/solid/trendingupicon.d.ts","./node_modules/@heroicons/react/solid/truckicon.d.ts","./node_modules/@heroicons/react/solid/uploadicon.d.ts","./node_modules/@heroicons/react/solid/useraddicon.d.ts","./node_modules/@heroicons/react/solid/usercircleicon.d.ts","./node_modules/@heroicons/react/solid/usergroupicon.d.ts","./node_modules/@heroicons/react/solid/userremoveicon.d.ts","./node_modules/@heroicons/react/solid/usericon.d.ts","./node_modules/@heroicons/react/solid/usersicon.d.ts","./node_modules/@heroicons/react/solid/variableicon.d.ts","./node_modules/@heroicons/react/solid/videocameraicon.d.ts","./node_modules/@heroicons/react/solid/viewboardsicon.d.ts","./node_modules/@heroicons/react/solid/viewgridaddicon.d.ts","./node_modules/@heroicons/react/solid/viewgridicon.d.ts","./node_modules/@heroicons/react/solid/viewlisticon.d.ts","./node_modules/@heroicons/react/solid/volumeofficon.d.ts","./node_modules/@heroicons/react/solid/volumeupicon.d.ts","./node_modules/@heroicons/react/solid/wifiicon.d.ts","./node_modules/@heroicons/react/solid/xcircleicon.d.ts","./node_modules/@heroicons/react/solid/xicon.d.ts","./node_modules/@heroicons/react/solid/zoominicon.d.ts","./node_modules/@heroicons/react/solid/zoomouticon.d.ts","./node_modules/@heroicons/react/solid/index.d.ts","./node_modules/@emotion/utils/types/index.d.ts","./node_modules/@emotion/cache/types/index.d.ts","./node_modules/@emotion/serialize/types/index.d.ts","./node_modules/@emotion/react/types/jsx-namespace.d.ts","./node_modules/@emotion/react/types/helper.d.ts","./node_modules/@emotion/react/types/theming.d.ts","./node_modules/@emotion/react/types/index.d.ts","./node_modules/@emotion/styled/types/base.d.ts","./node_modules/@emotion/styled/types/index.d.ts","./components/connectwalletbutton.tsx","./components/dropdownbtn.tsx","./components/finalizevotesmodal.tsx","./components/gradienttext.tsx","./node_modules/twin.macro/types/index.d.ts","./components/link.tsx","./components/linkleft.tsx","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./components/navbar.tsx","./components/notification.tsx","./components/pagebodycontainer.tsx","./components/previousroutebtn.tsx","./components/progressbar.tsx","./components/proposalstatusbadge.tsx","./utils/formatting.tsx","./hooks/useproposalvotes.tsx","./components/voteresultsbar.tsx","./components/votecountdown.tsx","./components/proposaltimestatus.tsx","./components/proposalcard.tsx","./components/switch.tsx","./components/proposalfilter.tsx","./components/icons.jsx","./components/realmheader.tsx","./components/scrolltotop.tsx","./components/signoffproposalmodal.tsx","./node_modules/rc-slider/lib/interface.d.ts","./node_modules/rc-slider/lib/slider.d.ts","./node_modules/rc-slider/lib/range.d.ts","./node_modules/rc-slider/lib/handle.d.ts","./node_modules/rc-slider/lib/createsliderwithtooltip.d.ts","./node_modules/rc-motion/lib/interface.d.ts","./node_modules/rc-motion/lib/cssmotion.d.ts","./node_modules/rc-motion/lib/util/diff.d.ts","./node_modules/rc-motion/lib/cssmotionlist.d.ts","./node_modules/rc-motion/lib/index.d.ts","./node_modules/rc-trigger/lib/interface.d.ts","./node_modules/rc-trigger/lib/index.d.ts","./node_modules/rc-tooltip/lib/tooltip.d.ts","./node_modules/rc-slider/lib/common/slidertooltip.d.ts","./node_modules/rc-slider/lib/index.d.ts","./components/slider.tsx","./components/tokenbalancecard.tsx","./components/inputs/styles.tsx","./components/inputs/errorfield.tsx","./components/inputs/input.tsx","./components/votecommentmodal.tsx","./components/votepanel.tsx","./stores/usemembersliststore.tsx","./components/inputs/textarea.tsx","./pages/dao/[symbol]/proposal/components/votebyswitch.tsx","./components/members/addmember.tsx","./components/members/memberitem.tsx","./components/members/memberoverview.tsx","./components/members/usemembers.tsx","./components/members/membersitems.tsx","./components/members/memberscompactwrapper.tsx","./stores/usetreasuryaccountstore.tsx","./components/treasuryaccount/accountheader.tsx","./components/treasuryaccount/accountitem.tsx","./components/treasuryaccount/accountoverview.tsx","./components/treasuryaccount/accountsitems.tsx","./components/treasuryaccount/holdtokenstotalprice.tsx","./utils/debounce.tsx","./components/treasuryaccount/sendtokens.tsx","./components/treasuryaccount/deposittokens.tsx","./components/treasuryaccount/accountscompactwrapper.tsx","./utils/governancetools.tsx","./components/treasuryaccount/newaccountform.tsx","./components/chat/comment.tsx","./components/chat/discussionform.tsx","./components/chat/discussionpanel.tsx","./components/explorer/inspectorbutton.tsx","./components/inputs/select.tsx","./components/instructions/executeinstructionbutton.tsx","./components/instructions/flaginstructionerrorbutton.tsx","./components/instructions/instructioncard.tsx","./components/instructions/instructionpanel.tsx","./hooks/usehydratestore.tsx","./hooks/useinterval.tsx","./hooks/useipaddress.tsx","./utils/balance.tsx","./hooks/uselargestaccounts.tsx","./hooks/uselocalstoragestate.tsx","./hooks/usewallet.tsx","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next-themes/dist/index.d.ts","./pages/_app.tsx","./pages/index.tsx","./pages/dao/[symbol]/proposal/components/newproposalbtn.tsx","./pages/dao/[symbol]/index.tsx","./pages/dao/[symbol]/proposal/[pk].tsx","./pages/dao/[symbol]/proposal/components/governedaccountselect.tsx","./pages/dao/[symbol]/proposal/components/instructions/spltokentransfer.tsx","./pages/dao/[symbol]/proposal/components/dryruninstructionbtn.tsx","./pages/dao/[symbol]/proposal/components/instructioncontentcontainer.tsx","./pages/dao/[symbol]/proposal/components/instructions/programupgrade.tsx","./pages/dao/[symbol]/proposal/components/instructions/empty.tsx","./pages/dao/[symbol]/proposal/components/instructions/mint.tsx","./pages/dao/[symbol]/proposal/components/instructions/custombase64.tsx","./pages/dao/[symbol]/proposal/new.tsx","./pages/dao/[symbol]/proposal/components/minimumapprovalthreshold.tsx","./pages/dao/[symbol]/proposal/components/instructions/execute.tsx","./pages/dao/[symbol]/treasury/new.tsx","./pages/realms/components/realmsdashboard.tsx","./pages/realms/index.tsx","./pages/realms/new.tsx","./test/pages/index.test.tsx","./utils/associated.tsx","./jest.config.js","./node_modules/next-transpile-modules/src/next-transpile-modules.d.ts","./next.config.js","./postcss.config.js","./tailwind.config.js","./test/__mocks__/filemock.js","./node_modules/@babel/types/lib/index.d.ts","./node_modules/@types/babel__generator/index.d.ts","./node_modules/@babel/parser/typings/babel-parser.d.ts","./node_modules/@types/babel__template/index.d.ts","./node_modules/@types/babel__traverse/index.d.ts","./node_modules/@types/babel__core/index.d.ts","./node_modules/@types/connect/index.d.ts","./node_modules/@types/ms/index.d.ts","./node_modules/@types/debug/index.d.ts","./node_modules/@types/range-parser/index.d.ts","./node_modules/@types/qs/index.d.ts","./node_modules/@types/express-serve-static-core/index.d.ts","./node_modules/@types/graceful-fs/index.d.ts","./node_modules/@types/unist/index.d.ts","./node_modules/@types/hast/index.d.ts","./node_modules/@types/istanbul-lib-coverage/index.d.ts","./node_modules/@types/istanbul-lib-report/index.d.ts","./node_modules/@types/istanbul-reports/index.d.ts","./node_modules/jest-diff/build/cleanupsemantic.d.ts","./node_modules/jest-diff/build/types.d.ts","./node_modules/jest-diff/build/difflines.d.ts","./node_modules/jest-diff/build/printdiffs.d.ts","./node_modules/jest-diff/build/index.d.ts","./node_modules/@types/jest/index.d.ts","./node_modules/@types/json-schema/index.d.ts","./node_modules/@types/json5/index.d.ts","./node_modules/@types/lodash/common/common.d.ts","./node_modules/@types/lodash/common/array.d.ts","./node_modules/@types/lodash/common/collection.d.ts","./node_modules/@types/lodash/common/date.d.ts","./node_modules/@types/lodash/common/function.d.ts","./node_modules/@types/lodash/common/lang.d.ts","./node_modules/@types/lodash/common/math.d.ts","./node_modules/@types/lodash/common/number.d.ts","./node_modules/@types/lodash/common/object.d.ts","./node_modules/@types/lodash/common/seq.d.ts","./node_modules/@types/lodash/common/string.d.ts","./node_modules/@types/lodash/common/util.d.ts","./node_modules/@types/lodash/index.d.ts","./node_modules/@types/mdast/index.d.ts","./node_modules/@types/mdurl/encode.d.ts","./node_modules/@types/mdurl/decode.d.ts","./node_modules/@types/mdurl/parse.d.ts","./node_modules/@types/mdurl/format.d.ts","./node_modules/@types/mdurl/index.d.ts","./node_modules/@types/normalize-package-data/index.d.ts","./node_modules/@types/parse-json/index.d.ts","./node_modules/@types/prettier/index.d.ts","./node_modules/@types/scheduler/index.d.ts","./node_modules/@types/stack-utils/index.d.ts","./node_modules/@types/ws/index.d.ts","./node_modules/@types/yargs-parser/index.d.ts","./node_modules/@types/yargs/index.d.ts"],"fileInfos":[{"version":"89f78430e422a0f06d13019d60d5a45b37ec2d28e67eb647f73b1b0d19a46b72","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06","e6b724280c694a9f588847f754198fb96c43d805f065c3a5b28bbc9594541c84","e21c071ca3e1b4a815d5f04a7475adcaeea5d64367e840dd0154096d705c3940","746d62152361558ea6d6115cf0da4dd10ede041d14882ede3568bce5dc4b4f1f","2cc028cd0bdb35b1b5eb723d84666a255933fffbea607f72cbd0c7c7b4bee144",{"version":"abba1071bfd89e55e88a054b0c851ea3e8a494c340d0f3fab19eb18f6afb0c9e","affectsGlobalScope":true},{"version":"927cb2b60048e1395b183bf74b2b80a75bdb1dbe384e1d9fac654313ea2fb136","affectsGlobalScope":true},{"version":"d8996609230d17e90484a2dd58f22668f9a05a3bfe00bfb1d6271171e54a31fb","affectsGlobalScope":true},{"version":"43fb1d932e4966a39a41b464a12a81899d9ae5f2c829063f5571b6b87e6d2f9c","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"4378fc8122ec9d1a685b01eb66c46f62aba6b239ca7228bb6483bcf8259ee493","affectsGlobalScope":true},{"version":"0d5f52b3174bee6edb81260ebcd792692c32c81fd55499d69531496f3f2b25e7","affectsGlobalScope":true},{"version":"810627a82ac06fb5166da5ada4159c4ec11978dfbb0805fe804c86406dab8357","affectsGlobalScope":true},{"version":"62d80405c46c3f4c527ee657ae9d43fda65a0bf582292429aea1e69144a522a6","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"75ec0bdd727d887f1b79ed6619412ea72ba3c81d92d0787ccb64bab18d261f14","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"12a310447c5d23c7d0d5ca2af606e3bd08afda69100166730ab92c62999ebb9d","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"9d57b2b5d15838ed094aa9ff1299eecef40b190722eb619bac4616657a05f951","affectsGlobalScope":true},{"version":"6c51b5dd26a2c31dbf37f00cfc32b2aa6a92e19c995aefb5b97a3a64f1ac99de","affectsGlobalScope":true},{"version":"6e7997ef61de3132e4d4b2250e75343f487903ddf5370e7ce33cf1b9db9a63ed","affectsGlobalScope":true},{"version":"2ad234885a4240522efccd77de6c7d99eecf9b4de0914adb9a35c0c22433f993","affectsGlobalScope":true},{"version":"1b3fe904465430e030c93239a348f05e1be80640d91f2f004c3512c2c2c89f34","affectsGlobalScope":true},{"version":"3787b83e297de7c315d55d4a7c546ae28e5f6c0a361b7a1dcec1f1f50a54ef11","affectsGlobalScope":true},{"version":"e7e8e1d368290e9295ef18ca23f405cf40d5456fa9f20db6373a61ca45f75f40","affectsGlobalScope":true},{"version":"faf0221ae0465363c842ce6aa8a0cbda5d9296940a8e26c86e04cc4081eea21e","affectsGlobalScope":true},{"version":"06393d13ea207a1bfe08ec8d7be562549c5e2da8983f2ee074e00002629d1871","affectsGlobalScope":true},{"version":"d071129cba6a5f2700be09c86c07ad2791ab67d4e5ed1eb301d6746c62745ea4","affectsGlobalScope":true},{"version":"6c55633c733c8378db65ac3da7a767c3cf2cf3057f0565a9124a16a3a2019e87","affectsGlobalScope":true},{"version":"fb4416144c1bf0323ccbc9afb0ab289c07312214e8820ad17d709498c865a3fe","affectsGlobalScope":true},{"version":"5b0ca94ec819d68d33da516306c15297acec88efeb0ae9e2b39f71dbd9685ef7","affectsGlobalScope":true},{"version":"e8c9f4e445a489991ca1a4232667de3ac36b07ba75ea335971fbeacf2d26fe67","affectsGlobalScope":true},{"version":"10bbdc1981b8d9310ee75bfac28ee0477bb2353e8529da8cff7cb26c409cb5e8","affectsGlobalScope":true},{"version":"583b4bd4e441916be4bf152ee323c4ffa6e178fd25df2ee2215ef05c7feb5ad4","affectsGlobalScope":true},"d20f08527645f62facb2d66c2b7bd31ea964b59c897d00bddb1efe8c13890b72","5726b5ce952dc5beaeb08d5f64236632501568a54a390363d2339ba1dc5393b1","674bedbfd2004e233e2a266a3d2286e524f0d58787a98522d834d6ccda1d215a","714637d594e1a38a075091fe464ca91c6abc0b154784b4287f6883200e28ccef",{"version":"23edba5f47d3409810c563fe8034ae2c59e718e1ef8570f4152ccdde1915a096","affectsGlobalScope":true},"0e9c55f894ca2d9cf63b5b0d43a8cec1772dd560233fd16275bc7a485eb82f83","d53b352a01645c470a0d8c31bf290ba791fc28ade0ce187a4a50f5c2f826f75e","5f0a09de75bd965c21dc6d73671ba88830272f9ed62897bb0aa9754b369b1eed","2b34e7fcba9e1f24e7f54ba5c8be5a8895b0b8b444ccf6548e04acdee0899317",{"version":"06d2be99c3dd2ff52114d02ee443ba486ab482423df1941d3c97d6a92e924d70","affectsGlobalScope":true},{"version":"bfd4f140c07091b5e8a963c89e6fa3f44b6cfcbc11471b465cf63e2d020ad0eb","affectsGlobalScope":true},"a106a0bea088b70879ac88ff606dc253c0cc474ea05ad3a282b8bfb1091ae576","c98ce957db9eebd75f53edda3f6893e05ab2d2283b5667b18e31bcdb6427ed10","37eed30fc8318b8ac76eac6f41d0758a9d0bffd8f3ff353e3ad0f8717dd08d92","9aff68f1b847b846d3d50a58c9f8f99389bedd0258d1b1c201f11b97ecfd36f8","1978992206803f5761e99e893d93b25abc818c5fe619674fdf2ae02b29f641ba","05fbe81f09fc455a2c343d2458d2b3c600c90b92b22926be765ee79326be9466","8e7d6dae9e19bbe47600dcfd4418db85b30ae7351474ea0aad5e628f9845d340","f20ea392f7f27feb7a90e5a24319a4e365b07bf83c39a547711fe7ff9df68657","32542c4660ecda892a333a533feedba31738ee538ef6a78eb73af647137bc3fc","0ecacea5047d1a7d350e7049dbd22f26435be5e8736a81a56afec5b3264db1ca","ffcb4ebde21f83370ed402583888b28651d2eb7f05bfec9482eb46d82adedd7f",{"version":"fcb95c45150c717706119f12f2a3639d51baa041cd5bb441eb8501e04b52c501","affectsGlobalScope":true},"a7b43c69f9602d198825e403ee34e5d64f83c48b391b2897e8c0e6f72bca35f8","f4a3fc4efc6944e7b7bd4ccfa45e0df68b6359808e6cf9d061f04fd964a7b2d3","73cad675aead7a2c05cf934e7e700c61d84b2037ac1d576c3f751199b25331da","8c3137ba3583ec18484429ec1c8eff89efdc42730542f157b38b102fdccc0c71","74594bec8a9fa1a2cd40268f9ab95d0c44b6080345da6e8b913939d19b1d57ae","94ca7beec4e274d32362b54e0133152f7b4be9487db7b005070c03880b6363aa","2d713cbcbd5bcc38d91546eaeea7bb1c8686dc4a2995a28556d957b1b9de11d9","bbf21f210782db4193359010a4710786add43e3b50aa42fc0d371f45b4e4d8d3","0b7733d83619ac4e3963e2a9f7c75dc1e9af6850cb2354c9554977813092c10a","3ce933f0c3955f67f67eb7d6b5c83c2c54a18472c1d6f2bb651e51dd40c84837","631e96db896d645f7132c488ad34a16d71fd2be9f44696f8c98289ee1c8cbfa9","2c77230d381cba81eb6f87cda2fbfff6c0427c6546c2e2590110effff37c58f7","da86ee9a2f09a4583db1d5e37815894967e1f694ad9f3c25e84e0e4d40411e14","141a943e5690105898a67537a470f70b56d0e183441b56051d929e902376b7b2","ddc086b1adac44e2fccf55422da1e90fa970e659d77f99712422a421564b4877","515ef1d99036ff0dafa5bf738e02222edea94e0d97a0aa0ff277ac5e96b57977",{"version":"2708349d5a11a5c2e5f3a0765259ebe7ee00cdcc8161cb9990cb4910328442a1","affectsGlobalScope":true},"780058f4a804c8bdcdd2f60e7af64b2bc57d149c1586ee3db732a84d659a50bf","ae68a04912ee5a0f589276f9ec60b095f8c40d48128a4575b3fdd7d93806931c","19d580a3b42ad5caeaee266ae958260e23f2df0549ee201c886c8bd7a4f01d4e","e61a21e9418f279bc480394a94d1581b2dee73747adcbdef999b6737e34d721b","9c4c395e927045b324877acdc4bfb95f128f36bc9f073266a2f0342495075a4f",{"version":"ecf78e637f710f340ec08d5d92b3f31b134a46a4fcf2e758690d8c46ce62cba6","affectsGlobalScope":true},"4ee363f83d7be2202f34fcd84c44da71bf3a9329fee8a05f976f75083a52ea94","a7e32dcb90bf0c1b7a1e4ac89b0f7747cbcba25e7beddc1ebf17be1e161842ad","f5a8b384f182b3851cec3596ccc96cb7464f8d3469f48c74bf2befb782a19de5",{"version":"5d708266116e778d6a4140fca2ac36f71d99b4c68bc3be63a45ba8bf5ade5348","affectsGlobalScope":true},"b514b82ee3ee4e33b40987d35ffeccfdf0cc050f9c4af29e04b50e54b5fb757c","714bc11c4ece2d28d6b70207fcefce4651c138cc77ce0b8147a6bab25c66b67a","72cd580decc682538544d8507f98c38c9201f83da228d2a0c170bcaa0937a829","08935635053203ba941035573962605b91376318dbce19e97c71354f2241e0e9","4f24a713856fef84913f851b9a6929570e2417c1c949634958db3cae98f54066","c74616881631c4278189bf62e1dbdd001d60b768a5dc2ba2682af316c05c9857","562e66c29f4c43b32c203ca892cd6d5b6fa8e70954d23b4d0e2db0eb3d6b698e","764f5b39a73fd6371e5a118ee037b685cec4ff2fc3579225eb57d0f82a38ab18","9ef16c926d9df3f0911bc679c9b4d33e464e7d08c6947cc593bac3ecfc716b99",{"version":"f1adba6d225db6b4ffa5172c9bc8a18990598da3dac3a33c86ba82cd509cb20e","affectsGlobalScope":true},"226afb272b10e93984d2f6bdccef184b0146d9cbbd448167e09292fef413a335","93865b0723d744eab9c00bfe7a8ccd962d1f6a2047e4c7eecd18482ef8b87e8a","72c88123ada80953914b43a0d9810bb0ce8e5b25cd8b7965bfb6842ffff74f05","2766dee26ea113e9b491b7842cb44df57c4d79b17057b42607e09fc174bd411d","2b4fdbc425984dc7a29c4fce80f656698363f3c9cde55fbe26e620bd3fe0da13",{"version":"7edacdb86affe13b999e4f8bb6a87ebcb9ea2fb9c8f8d159321dc113d973364f","affectsGlobalScope":true},"cc0a1cfd544b39afe9931bb9d93b40572bbd24d837eafc44888edc9c72b00d23","3d0f430227f44476d4c66b603d0b7d126422a2e8978965e156f0028c6bbec9e7","3ffe1dc2de9e55a28e0e17d8974b1f4e1b222911b84b6be6c4f0ee14723851ad","ddae6e637e160dceec077487bbd9176f4a8c60b46596a86fafd3bbd1882f6232","6c129db1dc74f8a65d99f01dd9f08fe3712817b770c6fb9ff1d7686288c8d3b3","e990bfb110c37fa629d8679ac8bba89d36a566d2c0984ab0190a70e4d54f5576","d2d90639077deb5644ec7cee738126779eff847692d95ebcd9f76d6ef2f08cec","d2bd714b146c0c6c27a0bc9f16c96522ef1c829923fad14b6a33e2580a012323","9ff3163c1203fb6197f0224e546eccabd46d7892c2b2d77ff6719f304c77ab90","a2495db5f46921bbf7ebf294e0683df709f2be4d9468aa3fe2137edb5a63a03e","5d7e0d8f75012b66c3d505af116568163ccd415a3b59ce51bc751d7797cc5f73","d57766da0e70a20dbff6d4f4a2ab77793302817e46f5d7ea0677cf9c9c9e0537","a8c3c3a27e8c7b93d5bec55beeb89f31c293c516e9139f4cc2eb60f5a06acd89","dd1b2492877b4d5b42fc2724d18d9805248efc5648be6ebea3c70b8bbff0a804","733975885b2e625092587a54edaec7bc023ec33dd22e03426542f308e04803b4","358e67db39c642ac453310968e5d2764e2f42a854e7ba8fb17a4347a40e94c25","6e5f5cee603d67ee1ba6120815497909b73399842254fc1e77a0d5cdc51d8c9c","19feb76bded29cc201568f945579b8fdf6f127362934f1ba0a77a9e7143e9b99","61c414ea61cce0b5320f203678d6a0c72befad0163bfa1004df9cedfdce73985","c26df0988d0476dd990fc069f96db0a51c63672b6abf6211823445575fd88008","73d959256b4a510edadfff37562affea8f50d477fa4900908d2dcfbd239e6d79",{"version":"68883138d1fbe09c3b8456411c9b7cbae9df330cd371b9d713252a63788a7d8f","affectsGlobalScope":true},{"version":"fd83ea3cad04f68eec8410c4a614b78899b9b19efc2327189950f52d2f54219c","affectsGlobalScope":true},{"version":"d0d8bf23d0f82c4d630a05c27b993bbdbc2c372186f51a61aac48bd2cb306ec1","affectsGlobalScope":true},"608f53422a1ee2658a484fad060307614f0c3359b1a3dd228c9c24540655abb2","9a2578d97903ebb95888128d68ce3c13f539f168332c4888b267f2c67d77964b","325cf4f6db7cb37bc0f0e3f2677781513297dad5d52ac672e5da3bb1ed2427ec","8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","3249dece06e3f39e8887543172351d2a52fc3c4d7101280f0ed199f1bb3a9ad2","6b47c480f691540dc6e41d4b4e407269e2ada29ceb86bb683d52ff2110d7a5ec","b97e8f70da41b30d1259e69808e956f1700f7ec06eea97cfa50f28ad7bb30e2a","14ed84426e6be36081f3394952d3aa7001475eb51b49da19ad1379d7f0113970","bc6dd50ac2fc9a7ca6488811b116bd0ddd606338db0bb97852c8fcf757e2d7f5","e5e2d6577c8ac83e55c95b29a67466c21a01c9ce7da51909fff0182ef209b0e1","7fb448c6fe70ed69bee007528167e72c6bddea444e2df6dd1abffcbe7136f6a8","229d76082578d2d54ccc5c185f6a504e2f17d2ea8b825a58db523627313c3723","b80c780c52524beb13488942543972c8b0e54400e8b59cee0169f38d0fabb968","52b7e7aa14a72e4ec83c92d6a2b506f6f5514f4764bfdea8e87d90f1cd42788a","bea0b7ff48a6fff813bac29ef613339091e7c55fef263bd57ca963ec10c6b052","46da10512607b9042fa4dcf0d8c9149027aa7c5144e6fd8de209cef8fca3dd32","56b4d0000f46aa21f407d414ab01d9b64906af4beb0aa9f7e92d562aac30d4d7","cae6f5684a1fa2e8144e344d83b706b80e4b0515755efefa388bcbef4623571c","be633f6b6ca1b946e06a34e15da8e2c7b28ed703c70b53317472a6187e08a8c6","1d7d9e42cb2cc36dfc7ce35755ce67d5ff0f4a2ef72d3019f4c461bfd22123da","f97edc4d3b4dd0b32766e9633086497565c37657fb591cc84a35864db4bc2fc5","880226db55671d8d4971215a147dcff0534f6fe2243faf879b7f0abdcf745cee","e12f73f15418cf9ccac96e2af6e7867060a458f9b8011286f14bb93ece989d1f","03da7e82f6dde8b92796ba5940d2837c54076c2bcc07e1e1712cc0154983f3e0","4df0891b133884cd9ed752d31c7d0ec0a09234e9ed5394abffd3c660761598db","b603b62d3dcd31ef757dc7339b4fa8acdbca318b0fb9ac485f9a1351955615f9","5982e54644e80c656b76228ce88a79e43e25af8867a3b5afceecad2d54e684b0","be90b24d2ee6f875ce3aaa482e7c41a54278856b03d04212681c4032df62baf9","78f5ff400b3cb37e7b90eef1ff311253ed31c8cb66505e9828fad099bffde021","467f923050c865b709cc9e1136347c50879ac4f1ba17d0a246eb6b2883bdcda6","71c67dc6987bdbd5599353f90009ff825dd7db0450ef9a0aee5bb0c574d18512","6f12403b5eca6ae7ca8e3efe3eeb9c683b06ce3e3844ccfd04098d83cd7e4957","282c535df88175d64d9df4550d2fd1176fd940c1c6822f1e7584003237f179d3","399a35805f9274c83878e64eb12edbd6ed943e36811cb474591bf170d045a0a3","11a9e38611ac3c77c74240c58b6bd64a0032128b29354e999650f1de1e034b1c","4ed103ca6fff9cb244f7c4b86d1eb28ce8069c32db720784329946731badb5bb","d738f282842970e058672663311c6875482ee36607c88b98ffb6604fba99cb2a","ec859cd8226aa623e41bbb47c249a55ee16dc1b8647359585244d57d3a5ed0c7","8891c6e959d253a66434ff5dc9ae46058fb3493e84b4ca39f710ef2d350656b1","c4463cf02535444dcbc3e67ecd29f1972490f74e49957d6fd4282a1013796ba6","0cb0a957ff02de0b25fd0f3f37130ca7f22d1e0dea256569c714c1f73c6791f8","c0028a4b7110278e7940dacbee026257edb24b22a623dca3eb59cbfbdecd7f4f","69599c1483cdf5d35a577506bdebf914e77438a2af88c4bec9a977f0f6651a4e","660ae3a373843ad4ef84cf68c8d46350333ca7ee27f60baeae304ce9a93e8643","06d49a030aac03e90a3be6603b050b570f30f57a060db955a31310aa4c236436","370e0d336c32096a9b7fcd8810d3a13624583e4226cdbbad0d6ad8ad64a332b5","4b7a1bb5c7a9fa4275c0130db41f0f66fb7bffc7bcb28178174a213a991f97fc","6117583afc03b8469cc3bfc76eec89e15ab4d367df8ecdef21c873b5a0ff9ab1","aae1685dc0e66205bfc5a3b303cc1e9011c8d5a9ee1fce6fc36adeb17f334083","07361c73121e4fc16bb32a89f7aa06e9f1713e369fa48791b184d539acacbed7","bb513d41884a322d6274e1f2fec8abb3971c1f99c1af6a8d8ed4afbc7c146fe9","d95c9913e57cded9f638e398f59f8c9448ee8947d2a9f268049eb21946035ac8","591c5dc9e52bb2da7fa5889ab274f980fc63a8e699c74dbd396f0d1bc9463406","2422b1d1f400064b239e07fb64d5ca7437d21540aa4ba2656cf9ea1444f6c8e5","ab82c9ef856c8ac190908a4e64318d4b51811532069146e8eec727247f94a415","4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","daef0f776100312debc3f182dcff0549ee1f88e7e23251726fdae3fd3e608fa3","b7598425eebd93f4170f6a52549e8bea9dd169acd34ed6b3eb39e024bfa35a52","60c40081fb386f773ee3475520b5af676b3107d22a02042227a2265f04f016da","abd3eb2875520442a92ed7641d9e4895d2a981f2fa4cecc460b8bf1e2416c84f","3249dece06e3f39e8887543172351d2a52fc3c4d7101280f0ed199f1bb3a9ad2","a5044b2b46cf234058f2917573cc3a000df896b3b537597b1572f942054d924f","1614a1ca6221d0dcc9bb4df2e280edd49e619eed0f826ea123281af36b408cd9","aadbfe13d83fb98cd38c51ce99dd119be8bc7cf46c87e3b04fc6729cf4fa35fc","4064f4d87306414dd6b4aea958e46a1e659c9f3fe231eec400d0948441d76682","0c2d187ce8b461daf1036c391d92072f6101331f97d8545892b0e247c2833b38","d46fb070057489106446538b8ef3ae8573809fc28a0bbaf39614c111520873cf","5a44f0efaf82fe9f865f61b2020c9f78f133b173fff5a79292096a8f8914910f","afce3229233d36eb877e9bf4f5f1c7ee828cdda75f0d108ff0b4dbc2dbfe17f3","42942638bbd3646027c2f27025148f8b8c675be73319c4b0f4c98d55a52d8789","51f475cb0a4a91a4359429c7eb9e2871270a6b990a2473eed846626fd2336058","bf8be89d1adfc9f11e29e4c4149d3bf13d8d89b076ffdb09f2360f0adad90184","847ccc20490110124a2252c5a705dfe1355921470d2bd6da9af1d4943e54346b","49d35efcc16be6b9b23c5f61d8bd3867e6cfb61fea7fa69562250cc394900dda","e62fd19d48077f1de0be67b0530f5623ba9eb1b4e7f8c3a1d1ccb9295e58ba70","e6ff39c5eb69d9f01557f478a7d0d2bb7fdbf7c03770b9632a6a2820cb89b7f9","6f6e0910e0f211757febf2f3656d48d048f48097e444d2c9435aae4dd9678964","dae492af52685ae68e6f6f90ff2a42422fe8a8fd99acd9b40705a9470c4f7bfd","139db12276e11f90df65778f7513bc3f85957d74fd0d1bbeb19e4a1dace4f662","2884fcc91f5d42a3288a75a558b036923e3d926354fa0c3fa0251c2c3d4b54e8","cc486017c03c0eb004237288602477ef2c636eed71d7ef0911da01c856b52ad6","7e6962ddd100e59959d982f7e1d0852bb157f49fc64318d02cd5988dfbd213fd","1a33fe7fba89d4ede6eeba8239dd1288df1b00314aa537c461f9a84d69395f3a","e94c87113bdbd06f03007baa703bb1b479e26fc8045577328478f4d1499beb27","6218f145c81ad3dc715760dffc687c47c12e7deb9ea92337efed3eb025849b58","faebee35452b1b6c08d54169f027200ca6c5a07477e19cfec43ef0a7c1350779","070ec7f6c859fc28cfcb979f1f29c3abdb888fbdb34cd9f8bca821d6dbda1810","a0b7830e8a09fde6726e46943a13b4c743e18faf46390df0fd779fb9413d31bf","b4a4a1b442872ac195e970710000a5643a7abce88b24b7ab909717dd1d8ac643","b59a90fa800d02ee8685555d45cdc08fa425f00b0f9fefa0a81100f57798b1f6","28354687c0f1267fd8cfa0645db7fc340a180cf7c299a58f91c3fe99bd6cd6d7","1a77d55ffb93d46253e8c55e475cc82a6aaed46f4d9f85e61acee3d35fa93db7","757e0a13f76a367ed30814059c84795f6b1724e4f4e5a63a96742c889c86962c","f418555b5fa7590b1241cdc2183e7e107349b8c5ed6d971e41fec3536503d61c","35cddc840baa0a7f66abdfd1a4f21427cbc96085a6270c07c30a4ef0a68a3495","01b50b0814c40b47a13dc67f8950c1c646f94081af3b7c71036f9b23b0e1bf9e","338fe401147d224018a0df1b20976f3bc4fc1439c5d03d4295c538ae07a5d832","ddb0530aee314c56b2ce615e3efad7932c3407e7c5c319cf8037f2c0b74c3fda","ca1e5a8fedb07427f7819f1946d46b1771890aa17837a73b14716bd7e7ebdbf0","816ff4dc1cf5d143551a526e890764532421a7b7f2de5b5589402ed1b4e81a91","3249dece06e3f39e8887543172351d2a52fc3c4d7101280f0ed199f1bb3a9ad2","0d80232f8e92e0e2a665c25140f1efb1b00e2db9b333b671f022b97f51d5b3f6","f2660f82aa48c3a390293d3cf4ef399ac7072d1820e8f6099b64c53c43cdc9df","3f6bcd4f5fe06d5b506377f8cb8a25de7478f1805fa547a57f28797c7990478f","6d18bda368eca05b7622a70b1219a46563be0e32a4c270bf5ae14ed40163980c","4d3862a3280aeb32c03c945cc441f4047e4ef928d4b295d842d115cefb82ca51","6195acd1eb973cdc31bb3bf5d07b7daa1109d69656f7fe16c51e4ea8a1a41d26","b7ef345e83601f07a3afefd56238d2910e3eee4518b345f3ce1dab2ace8cd84b","2e6b000b62e47ebd0c1592f72afc9fa9c58339950d51a1cca201b0a82573a980","22326678a4d4edfb2bb90d419d8c0ea3a3366f655cac9c08cb79326115456b18","173680298b05409aecf21d6533f111f25a70dd6dabaf40bffe592ed7d4e522ec","17772737c58ea77531f560a5cfa3cbb7fee82d1a2e75850bf4310154e432353c","a226b02519cd880218548632c481a71115fdd0630c45a5af94b090e8ff996fd5","609023afef3cf78fef0266bc8bfd16e841a54b9e71afe11c2e03fc78ba1f7711","bfd08ecc97214003152ed8bdc833b74a70d1a02a759738c13e9353491d32fa8f","0df7174efbf78459a2ba16adef3da17bfa676b1aa96bc844376937877426a826","ca1a105f0bc654bad1e5a49ab971e81b61e5619918c7dd8096ebc8e7bd6b0cae","aa31b02250bd1dc31b11d39acc82b257f67e9829ffab80822f183d997a5a3c17","5028ccc9fafc0f82737443fca154e5fbb561ab7083d04073389a20d71d52e246","67e6d9816a647dfdb6bb7df2583509741e749f1e7b98c7c0fb51bd702275f264","58da69a7a0fa1eb7abc7342baf611361444b67834d15e8a4ae168bbfe527d055","4e54c6eab7c8e05d25b9253f54355e05d914f13d58621b2b60749a959beaee76","0b46abdbcb2d9b67794068f7f16516052ea46cc78cb5e79da460c49e659cb1a8","6d0b45425e4ac8604cf85b9f5f6a773bf1f5a84182683efb59f789eee37f26d9","f822706e3514b1e36b57127b57ea0e7f1ce7c10e2e24842a3f2c5b4b8569c97b","d03eea1cca6edf45374e795278dd24e4851af135a6cdeb16124ebebc467b2b6d","ab35dc841f5607fdbe878507e93de5008f4e1f6fe381e6b4c9e980ad9d2d2e9d","f6ed2a22dcba5a21cf252b330e380887f26a4e0113f395533df5efb1eff08e9f","448e5eef354833c0c10677b92179cb2ce3990e491117ab98f96f036585d4b551","43cf26a1c05dce4c14c4552f70454cc69eb710fdd092b4518958120f3fd92463","2f57a873460c3bd4f3fb8d03df949e5bc6b100eaa088bb47f289584aab22ca34","d3d7334fb5523faa8cd53e6154658796e8651eb4ef5573cc2ceaf52cd0af9d97","ca80a174c903a2209de408dfc3d4a86e9dd8733ce2749104db3b1c8be158f99d","68d2d97ca713169e0e1bb85f5bbf5956d5f37c7e8602eab6756d072057b41708","74efe63791c2d2777fa3ae5ad1b812deb3cdc3a0e7c6527fb1c33c392b8ee7ae","3154a026075044aa102298fe9e6a7a14aaa26a06270680c7478a1765af8ffb09","fe9eec78bcb931ac8d4670e4f675ed78a4e10ae9b02e267be06a6662116ff5bc","1a29d889b36eb264975cf952fcdca10d9c5369f803c362905a37718f7a648a08","64cb839765fd72849db8a0ea421a73910ef03eb86920bbd97fe5bdc176497035","3249dece06e3f39e8887543172351d2a52fc3c4d7101280f0ed199f1bb3a9ad2","cbdea9cd8950e3e6a7ecb3bb3d8ceb5669c128693d19d73758f2fccb23c465ba","4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","3ba32d8f95afee3533bd255b90de8c7f7dfa85cd0c8963cc7513aca83e2cc2b1","2fff7b80f6a8f16bf3309bb4bac741426530235adc6550d9bec8873023022d06","3aa5714245cc32cab16b2cec25a851849c91569b9efc97021e81c339e9779d28","bfda33a9bd005e584189ee79034ed282163b0c90eeaee2aadfea0ce1c733b0fa","edb93374b4ea6f2222aa4e2f04e90793826fbcb4362c5224b3465afb2886d83c","d4388e807950354169d445d7f2734eee5029624ea474c5b0c30ed71408f68033","1da46c4d87aceed16e759be7a1f80318ea681b487b65227c8482f63cdd443add","86fc726327a074f88e870235de004d02091c64cf76a965040eb7b6f046e72a9a","75f85cda5ed6def82fc221cbf1f17611baed4f187a2b86c28a558e5a5cadba27","1709b56ae20f3171665b5869763035c6c5edac9aec038cf19108c623c915d128","3bf1c09f3bb437d0f73445e346a36c5036b37ec0df7116e21cab5f732b4bc504","b0fa8fedc114d437d5437e23b3c2b28f5881d47243ba5b01345d970c32cc3dd3","5fc91e4c9d5fca9d1b2d9b99af547208db2716cfac1a7d23244f77020f36d441","b56d43966efb421c3cacab86041c2dcfb9e43494fb31661c5ddb9fa1167d2b53","b7d75d4b1d9255c2b41ade14c8edc97600a3805e82e47b367e6a7f9aefc704f7","36637bd48dba4c101bf48751ac7c27cb17a8748cd0255097e767bcce99e858bc","300ed0ed3134854b4eda394cb2206a1ca55366678f0a02f73534be5ac37b3063","0e8ee18c23a83ef51f0cdf7103b08e380ac1532dd88167ee33f2e9187276b5a7","ade4ef9bc2c5b7fd1fbca70820a00e2e95bf93b0c1e515151e1bc23bf0a1069e","b118d91bdf5b4c05c6b2d40e70f5cbe4e3d32005d8e2a468b176f18b083a05cd","60d6dd6daa6314ca5c296350c6d458bd8bda908f23a496a832e806768712eebb","75245c1598a6051baeff2cd0d29d651927ea57660541b48072b522ebbc70a42c","4f7c4093c04f1ec43d7b8167e115e8686f085f5536d849b49b742eb85ad59fe1","2fbe79a0dd780551dc8f93c2619a537bf8d53418a59a4a45e2b6d7877ca52725","676fecee6e0f2974576f9750cbc8e6a8e8d8527d2306f4a86b93a508aae602b0","53dbe747852868d4191f98b6d4854422c34596e6ee81c35ed2691c9ea94f81e0","5fb68cc1f62cde377b950d05422d6c0da988b1e9185202ab60762787269f44ec","73f5a268612d2c209192babf9c337c2aa72c2f4d73235d4b4bf45e5b924aca2c","be3b0bf1b8204d2698ec9da9d3a5152809883fb27620c6fc2ca9d6fb82166bfe","0ef4c5c98428735d2a853819173121f5005c70f878380bc3e05445c681f3ca97","1c239b2ac4ba1877a58ce46640cd05df59955abd65146be33dbc45ae9e199514","4f2015707d73ec934ed686d0376266a95a72b0cf9232311bd0220d58075c5b28","310dd0d87f4441e9111e6df1cd760a6cd668c66d31967e75933f4c65e7ac1c03","db041f8d748028989cfb853a3e2e8df3a119080ce9388126d5fd2d6095326a7c","342d8efa6056584a87cc9de5f3f8e2c6f50c1b56e32e9d0eb109820b4d327b4b","bc8925a1d55b3fabc95672043efb4de9cf29b07db2f3fcbf711346da772a897c","342d8efa6056584a87cc9de5f3f8e2c6f50c1b56e32e9d0eb109820b4d327b4b","58322c151ede820bf2b7754505901c8c2ea2772072438d28356736593b8ad293","00294bc685bae59a7b5461403a4b49646524eaa46ce4624c2e7e795c57c870e2","1e4823a7cb526d63d4e522bececf886d0b346de2751b6db538d8287f3debae50","4e502bcb076f2893cc41122b4ad730c2492bef1f1ac2b947252925691fd3d709","255808caec502477d018c3b96db804fbbfdb7e90d5f911f88322000af090634a","8a311eb46816f086c15d7ac3dff57504d42336d8677ffc950929e45b770c328b","a10a821b2295ee948658e33de5454dafc25d6eb82feeef3743048d2899975e6c","d7c52ef6db35f7df4bfe7b7bab0f8b8f52e667de14e6499f338ef97eb62a38f4","1507f8bd845073fc851f649c6182acd58733c3b018fca820d62623b92a0330cb","3ece0319bb261935f1476a59cde5fca4af3a4edd05c3d5905b111c5337e583f3","4868e02be7bd8d95d63cfeea78d2f541b84f33180f977ba16c96d041787bc57e","7b3127c73e34a47a6e8645ae8f6560efc9a82ee7ac65ca677491f83a625f902e","5e40e927fd7427c9ae0fca1434b033336b8429c88f40fcc6808e9944f3854323","2d7d2d542a30602ad869c89e497ffc1d45ebee0352f3806833bb1a73f88a26a7","c592bf31cbb437c8fc1e9a9763c66578d6b281ca7fcc15d28da02014a9103b82","fd18ea34c9036330d6c8e58e476ba6d41f465a2c353052b36a7c41dfac5807f7","243e7a67e601d6914c57ac99fc0be61766632f703800ce4b6a04923dd6f3f6d3","70a8103298df884321586a7d8938ca8acec7995bbbdc3aba501cb287975624a1","18cabecc5191ee6d1b9a70c6bc95de8ed17b7b0d54be541a9e8b86c6624fc585","d0c577f937e63975a5eb9a6df62ce9620e81bf1e0057888708538eac2e5bb858","da0539eb9c41ab6854295f94b91646fb1959ef85b3c043791bdfc138b456f8c4","8aba8006c4ce44aa6f76d1e6c9e13a8659b1a8d5de2193419d280c3048e167fa","0a0b579a28a4f9d6ce843bae63d803606700ebf68402915aaa876b66b737cfac","686d54e5fe770390525b70ccbd001035c6d38bba67fca5dd2999b29387f41775","ebeae543e908652fe836a484d0328a12471a97b7cd63cfcfadc2302f95f7ff91","2ab52e693063a7097006707a5a11315f89ea1a21b6d0bfab986d66a64a8651a8","c5862f08e9c9f70a44f5c00eced2e45a94d70a02064ffbb1a7301123d9a7f31d","da403c58241af126251b8359985b2559c32d5c341ab7659650a1a46e9ab56766","2db7c4d47b5eb775dd6ecba44742591fbb8c96057324343bf743ded7e78b7243","8c9146a883ca0e3f1aa03ed8a3b8fc7bdd9e768d8f323771d382678b14e19a8d","99076745fe8e7394762d5742784b6c8cca35df73d2e6cd06679feda75d9d8474","5a481927ecac3d2bea3a4e97feabce099e813312d21f810ef0fc4bcb16e9a625","284f693a38f4826e6ec6dea394d792834c84ae07815d8e9e4edc25e2a91582df","6d9f5f0eb9d6ef30a23df05a31c7cede4873c49ce5ee4d119405f0887e336d12","b1387919215fda078f8f213675258ba2649a136166a7b58fe3b57383f29bceca","e51e34b4d8bd97569a7101c1b77234f6a1272cbb5bd50ed649b1da3f71070300","b562eed438decfd54e66977c32672e36ea5332118de4255fa8b078ea2aa60d94","d64e123a450152bd381a8371224f45c873a218accbc5032b96fa7efffac051bf","3cf63bba2868a5b90e9d0031c3501e3c3b50f66448e6ac48639b0d5bff9475f2","5a2194569d32a5cadd019b3ce2ade4d0e2c867b0fc2a48691940b7dca6ea8887","60c5577050b9e230486a60cf05ae4ae8ce9c7bd661061bacbb797a58cb561a66","5d72971a459517c44c1379dab9ed248e87a61ba0a1e0f25c9d67e1e640cd9a09","02d734976af36f4273d930bea88b3e62adf6b078cf120c1c63d49aa8d8427c5c","b1601667a5daa2b846d642276c318c8cec7b7235b3494e786c2a6a92809a058c","c116c5ff714c9c491b13616c1a8b2e69394dd9e6f28cead2d41b675860c0d99a","702a118d48a3dcef2324d30ac84185ab22717e118b0543dbe151972807b80ac2","ad2023038c6b42e4cbc195fdf874659e27f5c99191caf2572d2003fcd1dbf695","7f23902e7e648f8fff1daa2d28dc29c219d80920199fd0318257fb61308f88ba","e756ac94c7de6b07a7bd4a8cd365ac40ee289586c974e33f3676b3ef48d61698","cc7f5deaf4e1e2b8dde0645ae824d213cc71834a0d58d557f805f443451664a8","2ac36e828c9e778acbd5a34cd57c144a875443b45d6cee38cfebb9e5756e5dca","168994907c6427e885018396248c567b6214e0daa6b62d4fc1be334affa44c56","a47fcf458fa122a20e74794fb227c31edc1e2ab4595a21eccfef83f27376275f","60b3b7499b678b4ed4d33b566451ea66c25a0ec34f9b1b15abbba4291819c3b9","ee5c308961f2cd7a22d5e365736520dd92658dc424776aded00a7c589af4c71c","93d72cd6f97ba8fa778d340091a0b25a08ea336a2ac79cd7d33086906b5c498e","1beb838d318c4aec78922af98f84e575f93f3a7bf706616a5aad4243c734c2a3","afd4e04029b5a734f9c2be5224eeb6d24d28357b619e70aed67cd777bf81d779","dae7eadd549a0bdb790e260994fe17b30b3774d4d02e1ecc9b069da23855b811","430a3823f37255500b7e14031a1dee3c96ad7c7a4692c662acdb64943e7850b1","1084d6aae15e0911517a9a567c54ca3fdf0a550a744d4a51ad4d3ec601e10212","51c7e7813dd1c1e7b5144ebff587c97bf8022bb2d6e12c6b3c4e3597686b8bd8","204f41659a02170064f953786aa397d89a0c095eb766f942902acb32780cabdc","193e45dd974dff7630cec4c07aa73963458dc9d137778a5890ae7532e1b71e48","a50599c08934a62f11657bdbe0dc929ab66da1b1f09974408fd9a33ec1bb8060","5a20e7d6c630b91be15e9b837853173829d00273197481dc8d3e94df61105a71","1657505fba9c49e14b9ae448370dcd08a671cb64b959359a8b310101538fb86c","b4000a0a525fa921e896cbdb32ae802c9684f0fd371b5fc69e7310f7918cc2c3","9df4662ca3dbc2522bc115833ee04faa1afbb4e249a85ef4a0a09c621346bd08","76106f66a9c3d90d1571da9182464ecfe0ef3b541d0feded06505c675d165c07","0d5a646bbfc9a7e8c69f16d653214a204d4518f7056c5c72ecac45d09173681d","b2cc95672d11e70e8237f52e4bbb33aaacd3b469088f6b7e171f238cb891cc67","08955a210c63ab8cd7e27028403a54760dcf9010b401946664627acc8d14cd4c","0baf3b364a794c5d00e97fdfa6da7298ce3533cbf4c7b0b3d831f85936d75aef","6c9bb2e6a6bbd1f2a97d23ffa80f9b583f6883a92e670e6f02ebe20234e2509a","0a93a88c97b04ae8f7e5260b18b369241ab292032d7622afa7a914cc07eba3ee","09b369e0621728733772ab1277891812cbfc71f0a0e520c21b55c3bdf4e94464","d0c3475089ca6e0e4db3b6f2f07e613c6ce44fe48dac112c8b4e13953a8b13ba","161e73719adcf55a378341b87611b146ff76b96c53c228ad0c9e48c876bcbf40","024d77dfe9faa588e031051d5cc667bdc77ff521f84ee0d39186140fa1704149","c95f7fc243bd30b30ce21fa0712a198f5d24c86d5fbdb43dc6563942141b67a0","b17a560a212278c7637d389caeddea89a382e03abfe62606be49a1baedaf8dfc","fabf8c56ae89c20931cdb1b73ce6f616d0278dd6cfc307989f1da0dfb8f55825","0d0e527597ed839aa40470e76d74986bc7efa845b0d518c1f16efc2bb05b11cc","a2d9d943eef68669e7718cd1de4f27dd25415265518dd5da5402666119a75a88","e3bd4f7fca64f279413d04eb09ac103b5f2881f6c57588d81413a217ffc9398b","446f332f928d0dce38a3fdbcadca1736bd0cd803cfd922c91c230d9f0015914c","a7f33b03586f5c94752d25c5d21487eb5561cd420bc983bc9f697bbdbef57509","08a80c8e6621da115605805b13ed0ffd38fb93f9ac4e38f10263c21deda53e2c","9f52a7a1692f4728eed184232c433e2d02873c395a45166c81b143c41a091000","4f827fe2ab5958e2196213d5f4503391ca2d57e4f3a4d5193821abb83eaf6f6f","57366c8ef1c5f0ece5bbd3378cce12f69f082c10433908b7fb467d3b2b923d6a","511401f94123d90d1b40b0e4bb43e4cc4fcf30ae639c77f77de89e56b51bde6b","51974f49af96c9c0036f8afe375a932f3e28d157554732892e5daeb7611cead8","696a3d6706a7f5248dad2496f1261e75bedd48632b8a09902aacfa214a8e898b","abed854c2976912613142f422ec939f93e84c82b779afe876144a29bc763954c",{"version":"08ccccc97156ba30de324d0cf014b10774630ce44dcb64a0738a8e29deb2a8f1","signature":"30db28a970d6a049067d524161b31b75db3aea6e9148b53b16a5ccf42a49471f"},"3192b54876effeb978785858ec960ba7c57f81171070b5a0054a011ca09795f2","869ac759ae8f304536d609082732cb025a08dcc38237fe619caf3fcdd41dde6f","0ea900fe6565f9133e06bce92e3e9a4b5a69234e83d40b7df2e1752b8d2b5002","e5408f95ca9ac5997c0fea772d68b1bf390e16c2a8cad62858553409f2b12412","c512c0e8912b562194a3664dbe894a13b962b7fdc5e6467049e5776c9c573093","9260b03453970e98ce9b1ad851275acd9c7d213c26c7d86bae096e8e9db4e62b","c47202f28798de29986d7c1fcca4be4031fb72cd1c0dab001a8c9b19eddc7e07","969132719f0f5822e669f6da7bd58ea0eb47f7899c1db854f8f06379f753b365","8854881635098c8842b5e489272fc951546b2767e4f447a88ca9ce50a6ca9d34","2cbc88cf54c50e74ee5642c12217e6fd5415e1b35232d5666d53418bae210b3b","b96e8e3de3b3f500d50aefe067db5e2f3d8fdd51dc002727038c1b19c483205c","5ea98f44cc9de1fe05d037afe4813f3dcd3a8c5de43bdd7db24624a364fad8e6","3a1e3199054ae95161fc6a8418ee28cd774f1a258d1b0ee14aa71c48a1f8448c","0b3fc2d2d41ad187962c43cb38117d0aee0d3d515c8a6750aaea467da76b42aa","ed219f328224100dad91505388453a8c24a97367d1bc13dcec82c72ab13012b7","6847b17c96eb44634daa112849db0c9ade344fe23e6ced190b7eeb862beca9f4","d479a5128f27f63b58d57a61e062bd68fa43b684271449a73a4d3e3666a599a7","6f308b141358ac799edc3e83e887441852205dc1348310d30b62c69438b93ca0","b2e451d7958fb4e559df8470e78cbabd17bcebdf694c3ac05440b00ae685aadb","f0acaf12b028470af87ac5b89a60fc15ce0557fee633b80e32a5e5f0770cf8a2","b2c0df25a73e3d587b010f16ec8ae03a5e13c2e49f246c11dd1f5ab86565572f","28ee85a2b8f3a035d72d4158b6b217ed05b714ff67e9880b5e6688e07bdc0e95","a255eb283713a51578c94cc849b39e629e4fa2e12b1aec15b2310f9c0411064d","3ee1b8ca041d8ee9da4b2d6160c8f60e396fc831a2c17bd19a5ec4f1ab201f88","41a4b706f190423fb86f0fe568b685dc59c4a76760b506b02c10a3eb70ff880d","e26d25ac80157bf4dfaf1b15917520d7d5aa515389c7c0464911ab0129e58835","e305ba550c25a1807b5c432b31563f897c82111c8bce166e560029c94df204c3","a3e0936d6b795d2fc46850dc9c590152942ee297f3271dbd3d8588c3983592c6","2013af14579369e7822fb5f20f01268f3338141d4a382d5969144fe9c6a25ccb","6b7fcb23322518af97092aafb777aefa9196a1db215071fe8f96ae6f6101b498","a98d81cd9207ec9f1bd54d454809a60b267043dd56b165bdb6912bf9d63d4759","cd1aff7bdfa3af19aae3a21c958b932af6941ed89af069bd789f9ba5ad43377c","33e9caf10988726e5f7be53d2a4ebaad8db16e51b5f81e5ed4dbb0068f3a88fb","cc0d535d54cfec869fe4d28b9acda7e3427b25d8d84fdfe495021ad4cdee301e","f3e743e40c9a3b22de28624c8428e1df4cedc2fa3b3d99c546ebb95592ba1978","f8dab311b48db5e3b4361d76a629201098efdaa1b785120353f0ad221b263682","261b2daf69470a9e9d1ef26aa5e0c23f1611af380eb5e5f031c776ba57367741","e181f3ee5845726fa8077b39742a875d4fbaeb35f036765f95a77afcf982f989","9227c7d1182bc93c52d39d65472fef3f850a0d0145e1fac7388758d995878ccb","d90fecb86a618f308931b2efa45d7beeb73185db71ff184c1066b4e5f5900771","69af21e2252e417f1f26d2e6b1ded1f20dba11f066ce0690c53946f4369e00cf","fd4ae6100e700ea9e36ab622968f12ec03792f98f7da286cc44649395022453f","1adede2d971ad167525195ed811b8e0af2d17c728350689c470b6d5c5a3aa60c","4486b61dcb644ea4527e90b5bfbe7600b9ed50371d334a0c01e8fe661a99d952","dae0c9cc1c106f7b91a726813cfafe489dec4fb1e3f4f7c684d14e5f99f96b95","bf5fe5a9da7069bca0bb83fdc31ba9f624ac778c3ce25a06a3099d83a92a4858","7a0958a789740f9ddba6b7d80fbb1ae4d97da910970c1d59965ebc80304ad22c","54c9f10d962306a39448b924e73e631bba8221b5797ca4b2dcfb00dca78827b8","7125d9240eb556853fdaf7c892b60c630f6d01bd11580a32e9f299b0b91ca4f5","b339079a6ce4d7e86dc68ea8af379ee8700dfb205da5546ffdd3cdfe4be43df4","a97af3b565a5649c8cd983df40878da9a4bc080cf1a918199574f82a25fa727f","6e5996003f6555309d988ceae5b9952f3de47fc6e5110e5d64c612d4f37cb490","f07a1939b55120050d3fb95633ac6f8ebdb594e63eaf8f14c123480cc4bec03a","325e48e1f58a88830852e00f4fe7c515afef925a1254975a3ba9c54c9156f3fb","79caaa6f2db6cd3a1362f50b48ebae937a234b421c66516b8c98282b7d713d12","e7f0aa1f6c72a395ef5ac8749c03a31b00ea527abca2db5aea63804fef2dbb72","edca8fd9f3960acb4223f2a596d1f944320193afb076cb8b96afc19fb83144ec","511a250228acb3131a8c9eabaf45a53c3015c8a5abffc1c2e8b37942b779ef31","496927b9d1a6a7bea58453b9749b878ee1040ddc22de6db6e689db11ec111ebb","d17c142d0bbf9f20c696ed0224d1740fbbda0b0211225f2e48732809e15f2505","67e2a7d5facca25f1df14bc4fa4167337405fdc98b47df49b98aa2dcdaff5696","e16f1bf72fcbea703f1b9dba81e6faaf65e29d24128f9e09504c9a862f41c9d1","2e47b85f55b2c6606b137b4be24ea386c58bd9621370d2373e95b530c955102e","1f9fcdb7e24f1f9b391bb9200e32f6353781a1c604159a6257e9d8f1df7b9bee","3c788378c8c8d9525c168ce0578d536607896146b88bb5f6670ab19834be30b3","d16aba532f5391ccb1a61f90cf8e904ecabf3105641c398df0f4a79cac27d472","8d3a30c921fa2ea91d1bffc7fd9b8c42dc74bc819c503208122bd2e84cbffb5f","e4fb93fd2ec72ebaaa1e657435d607ed155da0005e6e30c440257d9fa877d834","ad103c0c76b809a8830ca4f9a8c8cb43b53d578bc63dc8c63b1342f1de90f8d8","7e15e2f23da806eabcf4bd0f1e48d7a5baeface210f05837171b50eed7d2b894","fb5e2afe7c4f988b615085166421f8d88464f6234abcc740ed861eccc0e17ba4","652af46b250ee5144ecb0eef7bcfa1647c88fd170cca974bdff9bb315f349f52","790daabde36636c46a264b1628f488ae49b2b378810383a1059ee722e82ceab8","89a58d0ab59eec78a6b6532e5d748f9942568891619633c890638a2912224ad5","9af24ffe92056dea7acff1dee779be364ad35e5f9861ca417d17bfb447a0a230","8fab0b106acb9de629cc3f7bf784187cd59d506d734917c4f140d02f0dcd167d","d0ba3b6aaef0c96be907938b6fb2a3a04a5db59de34a40f7e426bc7f10bb46d6","d91c919538e393ed3c649270a73f239ea7cd9f312dcee7dad037869a6eb0eee0","fcf5f4ff294643e6ea5100d09f40668a3a8744b73b8f1c397fac4b17ccecc72f","af3bebc2d30fe79abc9a505bc890d16af72f8ea21ec59009e9d57c2d8f6e0b01","784c657f85bebb1a7d94ef05e10f1cad4abdf32798203ef8631f7c3aca2390dd","e488fdf1efc9112b9ae08aaf2be027c3cc5603b916582c45d73bb3885728543f","a43b695758408470608b548841c97ad3827e453fe81ad835e29b9871129785f7","96dd9a7f52627f94b64da26c1ce05d2350941487861c8c27a0014c67273c8a40","0e754d4ed9a6cd6c131515ba94f3f1095fb10ac3cb0c20c2cbeef9e895f924c9","72cac4a4359c6a5e2e5c0ece767455797e46871350324dfe42ab14238f675729","cd091878f6b6994d9307156bda8a4419c7c41c524228d9e830f5fa618d70672e","bd3bbe444bc7cc28757c7669fb186a9ba326d4b65dbf99e18b2b5b9ca66edeb9","b10c73b3d7703d2d870d35631428cdec737d4bbc06706b6fcc6f6e058b8e1594","2543b46883befbefe10c2de9f3a0e7809de7baa09e192edda748443fd15d38d3","01cdf83ab596024078a6ce08ff990770326ceaf16f9081a8e369b9bc5110cadd","bbb9a17f2654caa1d34f49428c0e48ca0fd0d9550f5f82da6f544c924afa17b3","a9d8ba2c15cd99f51aa291034a1afff2f67f1f88259d2162eddaa25e3644032a","affa88c9484982a9aa35b30480059dadfe98c3bbd92f76513ae2d1d7e68096d2","3436c4b40e71c333e253578f6f3176870d4963d5f4ec14862ba5e40794bef8bc","a02f786598d002e2e42854d9bb4fc5a4ac03589538055a0eca03c9ec7ad35457","348863ce75f819f43557d134e5e7ff11a8ae582ac879349cdf9156bb696012f7","235ede03bdeeb87ca21b68fb1398ebc4749a924e2a7219977b71bfc9c51574a7","ec83a163716e8a8c2324e3f6b64c907ba7e5247b43df47f52edef954232c0211","15a76d1390ae38fe474023a51778887a6e39cc4204f65519a448ed7d1931275f","d7a9a81362adcd395f9db48531a89df84461595d189a234796e85ef983399042","7ac3584d37571a5ba61326f50860d843072ea95673e53d23b0b684635db9db00","96354248b7b7fe792c9545b7b153ef20763677692e9baa9aa6d1afbb17376ba7","5dce9f1eea7d40ad9f10295dff47b7de6fbb24b33858c0ff91aa75043e9899d3","d324bb068a3a98f3d7ff92eed388935a5ed4bd46b7678c5bf057b5a2ee9416d3","fe6e76ab5933ca777c6ce422c7023d44799d4832a8c5ce35e3592c8430867329","cc5beca247a7da7286a82c0f3b84686a922d0a402ead5d11b9ddd0dcdef5c762","24661a3d44d16268a8ac8260f35651526c54496ed5f29e559c066b7b7e6776c9","7c7ddd5cfbbab70612c216ab1d1f982468118fead1d57948ed31b41cd692c2fb","461ffe558e162cb3e451654eed59d0090a267818fde655088616be907007d654","829df07ac748fd372b8bcace5b46e6ce0420d2fd65c23f64b86e8a099b69a21b","162ff76724612621b2623b71139fae21981b276595c5e93a909b464c6eaf6310","4ec0abadee52b5287cba7929ce1c34e81a67a046c0299908158497ee85ff20b7","3b60b6d30d8be5b573e75d148abd155fb74cbce0795055965ad505afa4b181f9","5ea0f9746d216da9d45885462fdad43ed26dc4473ac6d289a94661c9a1e7bbbf","96198fa503333a1856039319ad4bc45c6e32afe2ede6a050e23fee2127139a40","e9dbaa3c845b96ebd98a7a3c59296fad4f5cbe4c2e471d0c54a248dab6d575f0","63c5fe7a04273a69125008737aa3c18212a1276b3a2f3892080c346cb589a716","adca096d8a06e8fe1f6f8a1d95dd176e0ec2216f5dce683c9c3656a9bb1e1f10","fe5cfccf2b757c44e7251d6ac822f4892d63f0dbbab920da4f24b893e655e836","e2a5e2a231048f1b0a8c6c123d524adeb3eda1464ac2413fd039cf5afa57bc54","8701130ab14da66b4e908e13c3ece584b420399cc543bafca971c414059ee5e8","811e9e98ddaacadbbcc92015fafd5f5ce0dbebc14f3536cbe225094b1d61f885","f7c8e5f19d7159bde8f8e9c6561c6e517953457faa86018a7f963b72863380fe","c0bc4b28c78bccbb158fb2e8b3e37a86fee5f26b6098a857befd864790da7cd8","e7b604762369c8fa5ffafb6e238a2c7af296e5a25bfa25cb33191b525f064cd0","dcabfb44bd25183c919819f87428fc589b20c3b9586825ec456f94cdb67bd316","41c39405eb8d94777d8b30d2bb295c258391ac4a45deef8d2f569b29bb82938c","f5786f9b0a39c790d245b33436e75576990e41e995e1fff1b0919833a57f4357","970025f12906d26ea3c1c381199eb6702b9c8cb0bec44edc02e86e004cf95eb1","8dafc03ad3ee7acabdb9254c702b80755bdafa7d7548cc6ffc21814e83055abd","e0dba6e973edfd7a5d8a7307ad1e6ec014b51fb7dac507ae132d1f9429016252","cc2f61e4781ad29f2aa93d4850de1b1d8313f242631f10ca17cc99411eb63022","6679676c1dc90d9c371f3de8430bf070ed36d2677d9ce3d2a336b54c5c40c2d7","cf2b168364792895c95f8f98f8fb662f07787e518e6d25b0f7c4aca9927a1bb5","db115e097d9ccc281414e7cedebfb0435d5bb14022f147331fb1bdad09404885","aa78c2b93bce87b73fccd6726cac3cac4f62927460ff3495f2a05553b4c04d3e","298104d50f65103c256ad79ff5128141000e4544a6afaa998d3099bee3975b84","a99f5ced5c95d7603c94c66a4619dfd0e737351bf20757de516b7f8ca193cce9","341b20f291eecbfefa6760a69f7f3f18b2094edcc794f4e78e903a5f0dd86fa6","238dd354909fc4a682e0cc4bd0d1eee8ba03197a4efa3cb284e502355eaec8de","8a3156a33e38b19f00d543a9f7a96054a0a4b051533449fb04267b4e533f55ef","bfd14082a4db87c2847135aab3d617ad7b488b3e65ac82f1620742548ed630f3","e2aa5b5cbc067b485de95616efb852886f4a1a43685ed7ea0ee8e08fec961cb2","3d6d27c275808a7e8540b1778a5d3808542518acda03f5c1ea4c9c5831058ea0","f534c1a02cd756679611ca2b36431b51715a0c59a070d413e292dfa23b9b5c6d","e26cccd0ef5654714877908c1674bee29a8e53d60c8c2d82bdffc12cac6b0fb5","ccf581fb8928f37fbd6509a7d8fa0d32156fc4eb414f434bf81cf7bd6849f7b8","69b227120a5245cddb0805eea82a0bea405872bcf595d2fce9fc03dd16133291","d6f495bfd0020102c67a6f80e411b00b913a001c468001b7cbe8c592c748f301","eb4463ba66be74eb04aeba3bded1f485a6ee90bed8c28a2c2573f0c983834790","a76187885d25a8aed20f71760c116bfb89ed1612d125bc190ab25a1a7a87ed91","76d36099aa1a0c7ea690ee1675ac4dd86cc62e2643cd40c097899268f8d2f7a8","815d7ee4cb5383f94b88688b8f2d70ce3e5df4de147d3669d225f8bfcffad673","afc6a3b94e405b3ae5d5038fe66f3b2412300c93ba1250805ee1a7ad19964ff6","b168bf198df3af94f54863a77ca14dcdb67af689d4cd876328f7c70bbb7b985f","54a40fe6e389146cb444299ef2d7d6e4ec83b05a9df2e7611fd1d1d862b2743a","65ec140c7cde7edee0f611bc84ce505cfa71916571e76f5cc197ba1dcde32f2f","969a96cb343d30bd8a28bad66c77049aeb0fabd9f608ad82caee751082685eeb","b30e161d3bbbe3f8d15b0bc5d9b47d1935ffffc7ced1099fcd84536c906af711","3eaea558e36977f924d85b3207406594568053d7a52950081b7603d112edefce","4918bfaa32bc0f63380d84b19bf5adff3188797b17b055417bbfdb09bd531d1d","5750305060905aff115cc0a6f295357099856fe72d76a1193204c23b4b9417f9","98195f663b1c09572bf794bf2fd7351b05d5895ed471589c0c79ae2e7c7d6b97","355e8226f1a83a02c2c5dc22781defbcf171df314f6f3205318851fd4550132f","8abeb772b7a7763686fd699980b74d9895863a758f2a6b824edb3d5e5c235078","dc1e2e53c9935f62739ca37d9acd484e83fd25bc051e5a330c9be0acbe774253","71a542cb540a3c0d4d954d311937a4df56157b0797489ea5cb8a9e57f449aefe","49fbd0ae0b53936499ca6293450230273cf299e017ac4a1cc8de936d50d8e696","d1a6aec1239a47bbcbbc55324d6ed293f79d4554f6bb0e411e206a9e22c50aa6","4e81f6b7048f1022bd8dd7dc18e43b4aca8967c4ffbfc8fe80bd4277936e5be3","5ff6328b404fb34d2828b501bd16f75bf17590d2b03a66a469a0359da07a06fa","149fbdfda86091cc37779a6eb3f01ac5c73d6e6d34a71e76bb3702a1ebdd8bf6","3c1e92d9a7a0c81d02f016c47de63a39706bb0e36231f2e6727a08cbbef6cfa2","ebec459a7d4933732cb453254997b9b9e7d17319dd40a29946f985719e297927","394dec85f81a33891c71f4e6a1b9a40afdbe93210d5dec749a2791deb57df5e4","19d2786de07b0dd973e5515d84182d2734f1c1ecf602929f75b30081fb20fcce","728d3db3ffdbd649c96fd64cb5766993cc8cb10b4ef207403fb98304eba04f57","8e4ae5371abcf89ca3059e621b666f1a340db0575f0c8635431417528f2d6367","de4f5917a7bf2c62cd3ab4c171620fd6e88a4a92902f02c0808ae67793e6baad","8f09f0abdd346cdbb1e545a44c85dcefeb047d07080628562a328d3e88160beb","4557c1259460570a893f9adb1547b5bdd19948497740c53fbaf654b20ded5855","ddf8a6692f74ae9ebdece687ec1cd9ff63811c5af27381b40c7996053a1b0504","de735626154dab7ceb24b208728b7461aaa5ad8848152ab0b39192e7bc0aa4be","c6296acd69aca14033c815aebc1b4c7fe72b92e44145dfe6432fe855c8c7b463","fe7df3fef3d25c0455e7cd4f36fdff7ad7b4d2163e7285a74d6a98a6cb48a282","7e9bf7c76b60c4402bd48996bfb0d1fa552a576991f9f73dbb856d15b0346793","29199bf01375b374d516e5c8d5d8ce1dac3c07cc52b00eebc0a7ba7b05baeb1c","7696d51d4ce2f2f21e9f73214396bfd823bee6c57f65d39e6a2264c40dd021f2","4b74f4072dacae8ba4f21abf5d042e5da499d027b0aac8e2d8f42f5f591453a8","d3b884fb07719c9457497fa9d5146f7977fed29df303347ff4175f630b903fcf","665320db7cce83346ce47ab3baa657b3115d796d28d8f778a98e7f23962b1247","3f471b5d1f378519b8276a869cb746d5cfc9b2bf4d7e5cbd0bdec3f9b5f81fe7","4afef388856e350489141ad7d90ab0767a02954d53d953f558237e04c89b5d0c","7b16f7f12771f8e25117321d1cb607e06be688ed8db002fe2cb13b716d0662b7","9412339ec64000a6dce5898fd848f025d31ec3b446872070cc041ade876c0c1f","726bc5d2505bf6051e80ede05a576e21d65118c077a8407ef4f9eca8d5445464","05f2af853ef135671b9482077d19a2935e3d924debbcf2f4803110bde114f113","134078b75b0105535f6164680bd73f88f9ddaa84b7e0126aeddd2af7ea27bcb0","264f0b10beaa4141a6bf228d2e22b19ff7baa76b39388da319bffcd1ced741e5","98682512d496bb618315de173d7a25ec363676da2457939f42b75a23c5acab87","1471ca4439a9dc9787976d1c9ca2b913908f4029465c87372d2efe2069c375f0","54b8abfc4713160ce97f91758ee1e8a547ba67c7328177d5e4c40613a3e87f79","60894b9993d7e1a7be37933c9bfe96b228ebc206cada93a44bca9d30e12d8d8f","236ec9d640fd6438a08dd2be3fb739352f147de529b4aa1953e4d4f74d6638b8","e75ea841bf22a156a7ae8f95eb7b1d4479da8c291019148d0a643027356b76c4","4111a7444998538da1f6f76378412547281e30cc5a7249b32e7402f66f83a492","4b9a4b3456012104409fde7f7631be98068daaafe1c49b627b8d92f033960b67","7f7840713032b2ad3bbc379ee2d401489b4e563294f7c87dbaf2424a6682beb2","b52b80732805d494ffee704b80f689772e1db9440c1728b907f7b25b3328d5ea","a805a58a4c72c7d513295fa7102284dd9cf76c1470e1845a6d7e9afa4bafa609","724e0ca25a06f553306e33a45951c368346cf1fc4b26b8bf4bf88b1479131659","b0880e598b7256855af6b9ea2aebdf47372444114264b56da9d25ea5f95d064e","3ff6607fc3c3a85814ec3d6e05e358d99773ee7c7b5e9deed5e086e39f5372a6","62247290540b91ed85258d7e8c67c7786e38bc1111429da9fa42b1b34e4ffdd2","656ee3f8184b5dfb87200f73350e57827dba056130d15064406487c0993f0e6b","b14c19907984b69ce25e011e6391ff6a150bb31f344d643f2a3d5af9aeb4ba73","2b77a0e88653109c708203a50fa23bb50406a9c8c7f61883c92e009f778387d6","2aa50966f709107108e7ad733c129c81f9e42731492948a9c23d4f8a0de5ae1e","080afc7aa193ecf03c78afe2e3d81cc3b18fa482f1bd955b4dca67bcbf220eb1","7282df0d72afd1c283644f5827159ffe0c899850fe121811e6e7e3eb77416868","07c70d4602003ffd9f23f8f6fc2693b7cf024a323a6d6146b11659105ae588fa","79eb7464a0215c82cf6fdc55b2378dc0a3aed416f03dc647fb6956975d446ec1","8cd341d72d1ce25d33dbe1681a9a5f27fecfcf65d426a0d0bb80ce97a1e37d50","4f750b488d0d1019bf8b6651e68689debd6106312ca7f4fca22627fc2d0acc04","4307994ad4d3a8d842a7d7da76f45f84e5eeaf1580e9b6071dd5fa6b8b21de19","87b54711b1c9791dd95b4ff88f814b489089f5b128f29e8a5fb7f6b8123f739e","4763437e8a65ec15310aa20a4ec288eae3de1b94b9426336ac423fddd482d70f","e2398ace0c73a5da036dbd6cab98008a251c709c56f1b665b6202e99ae3450dd","1a59a1ed95ac47cb6d1798a4dee9088b847f41491e57545e788ede35ed1204e5","c89ec75e2ebb2f5c2dce2d5f85ab59951cdd217748a49a6e0bd102fe69f5eb75","e653e5173f22f243892807fcd85800dfa4efe84be40e0ed1cccd15d62e1c9da4","973c290e94835130e51e934d078ab80e975a7bdf5b9563f5fa8de080a06c588c","5abc40134600b35d15fcc7305d5bc9e29942c64dbec6413137b55e77b689da1d","85c3303460c6339a77ec37ed9b7e06974f6d8e1351181b3f6f0c5180e0e7a76f","88d761b9b53ee5aa4ffe94d18e1c05c31ba8778beddcb8418f303c184f4f61f4","8ad1059fc2cf09ab5208fdc50a974a1a0c0f3737d81c2aa0718c583716b49f7b","ded7cab1c0c297958efedb1c4569674117693e0ebb6e8a1366cf7a629ba490c6","997c088bb8c6e1d869a689a3db0157d3efea26fa3fe49ece5f01044321949769","aa4d9968e228a15f4f93ba782c05f19de5aab2598ef8acd819ce72d6f4c9953e","1c4420d12393decf20734fff3f09f44daea5433869633ed11e9fed9b316523ec","25e74c23ca9d123ad4726803694ca249b958270fa3e98eb3de7f339f15241a45","87537c5c41597d3355cd28531f715bcfa77b73f0622d49b28b6064b3c0ba0ab3","56acbddbfb96d3e9502c73d87f216fd16b9954ce7fc345adaa51a054bbd548cd","ea5f8a7e470eec67d9b3a57a311393d9b8146d59e7d3965fc2a07745aabb50d1","d75a15490d5dc9b8bdd209200429a4cd31c139b1503e22e1ef743e6d4fd160f2","fb238a904625a2a0942f8f0aad2c96d5ba7b684b59890599a10d73c1bfd3f771","35c91fef4484772dedb9c253a07ad91912a8e349838bef1e7c85b93b6acd39c8","b92d1f42b729031910871b073bbf05b8d68994a91dc43a7fc64f88abff16db54","beaeb7cae58c1b074e1e5c4c0cc4e205b1763f0e9f413d7062951e1cf539b450","2059b7deab3beb764a2368200ead025358b48fe470bd6850500785d94c8fb5cf","da08b48bf74446b6f988e95f501693844d59d445487d89d2364b8bf431c8a21e","17c4db14970964450f5bdcd1349e6f3035419db7f7f9f88ee966b3b34cbaa8b3","a9e6f34151c8629364892059c1689e2f99775b3642a24854d0330112d6892cff","bf5fb8cea51021d395c45a092c1e97534d498c91812b99fdb07c658cf5990585","c0884b5257b0e18721c353cd723e4867f2b64d0effc5af4f52c6714a30ee4738","c615c88d81af3ba04ee8ef37cdc41fb05b320e0f23819ce9abc0d180a4af884f","82c2d46b810cd51467e62a4f74d93e80726e79b5b3c781cc1fc3b67e7d1e9198","a924f64903a0515c07cf5f91863acc874fd5d0b32023a8516b009e76b78171ce","ff5b102cf991baa9e21be3cce64aebfb510fe9b3471cd567037af9dc58a8f248","4fcc356d0447930d7303a1e8d0da7ef97a53d380cd8170cc438ffba0f24f7bdc","f10667e81d2aac535c8afdc137fd7c05719376da211a1f1a6dabe693138fb282","833363b2d840ee09e6d8772850cb712a686470ed897e316ff9b47bbd82b2d047","7dfba9572ac89d7d734b8dfd50d30866f3eb50e6f9a2067f11aad3c4214888b8","53e48b2ec7b468b93a8dc467e51d01d841b24baa8a49236ff4dcde08d98de8ef","f4aad1575cae557fa6d2851d504782531d0ad22c09604ca3f1b9531783406943","26dbd763026dc42722ec5754dba41c7d97abd6b930c4d1f3619a25702a215f20","cfe4e2eb40376ccd2351262594d1721c1d9bc26730af3e61a229b43c99a87e4c","0a6be9f2955b63ba2163362a78c4af5a284e9142c73c86ca744c3a7028d35afb","c9c5bab53f5aa5ba3cd099708bdb6f2f41749a5723b8a1e22e5ed4ca11caa02a","6df5536ac89c7eea9167a8fdf89957215c41a53fd8797f8dbd06ea8b7e384704","8a670db838f69bc208045f3299891e788525d9dc5e60674522173138b90a57c5","c39caf7f94ce482759519c9de1e1983139171291b8821c0960bfa8e1f8c73fa5","41a4b706f190423fb86f0fe568b685dc59c4a76760b506b02c10a3eb70ff880d","e26d25ac80157bf4dfaf1b15917520d7d5aa515389c7c0464911ab0129e58835","e305ba550c25a1807b5c432b31563f897c82111c8bce166e560029c94df204c3","a3e0936d6b795d2fc46850dc9c590152942ee297f3271dbd3d8588c3983592c6","2013af14579369e7822fb5f20f01268f3338141d4a382d5969144fe9c6a25ccb","6b7fcb23322518af97092aafb777aefa9196a1db215071fe8f96ae6f6101b498","a98d81cd9207ec9f1bd54d454809a60b267043dd56b165bdb6912bf9d63d4759","cd1aff7bdfa3af19aae3a21c958b932af6941ed89af069bd789f9ba5ad43377c","33e9caf10988726e5f7be53d2a4ebaad8db16e51b5f81e5ed4dbb0068f3a88fb","cc0d535d54cfec869fe4d28b9acda7e3427b25d8d84fdfe495021ad4cdee301e","f3e743e40c9a3b22de28624c8428e1df4cedc2fa3b3d99c546ebb95592ba1978","f8dab311b48db5e3b4361d76a629201098efdaa1b785120353f0ad221b263682","261b2daf69470a9e9d1ef26aa5e0c23f1611af380eb5e5f031c776ba57367741","e181f3ee5845726fa8077b39742a875d4fbaeb35f036765f95a77afcf982f989","9227c7d1182bc93c52d39d65472fef3f850a0d0145e1fac7388758d995878ccb","d90fecb86a618f308931b2efa45d7beeb73185db71ff184c1066b4e5f5900771","69af21e2252e417f1f26d2e6b1ded1f20dba11f066ce0690c53946f4369e00cf","fd4ae6100e700ea9e36ab622968f12ec03792f98f7da286cc44649395022453f","1adede2d971ad167525195ed811b8e0af2d17c728350689c470b6d5c5a3aa60c","4486b61dcb644ea4527e90b5bfbe7600b9ed50371d334a0c01e8fe661a99d952","dae0c9cc1c106f7b91a726813cfafe489dec4fb1e3f4f7c684d14e5f99f96b95","bf5fe5a9da7069bca0bb83fdc31ba9f624ac778c3ce25a06a3099d83a92a4858","7a0958a789740f9ddba6b7d80fbb1ae4d97da910970c1d59965ebc80304ad22c","54c9f10d962306a39448b924e73e631bba8221b5797ca4b2dcfb00dca78827b8","7125d9240eb556853fdaf7c892b60c630f6d01bd11580a32e9f299b0b91ca4f5","b339079a6ce4d7e86dc68ea8af379ee8700dfb205da5546ffdd3cdfe4be43df4","a97af3b565a5649c8cd983df40878da9a4bc080cf1a918199574f82a25fa727f","6e5996003f6555309d988ceae5b9952f3de47fc6e5110e5d64c612d4f37cb490","f07a1939b55120050d3fb95633ac6f8ebdb594e63eaf8f14c123480cc4bec03a","325e48e1f58a88830852e00f4fe7c515afef925a1254975a3ba9c54c9156f3fb","79caaa6f2db6cd3a1362f50b48ebae937a234b421c66516b8c98282b7d713d12","e7f0aa1f6c72a395ef5ac8749c03a31b00ea527abca2db5aea63804fef2dbb72","edca8fd9f3960acb4223f2a596d1f944320193afb076cb8b96afc19fb83144ec","511a250228acb3131a8c9eabaf45a53c3015c8a5abffc1c2e8b37942b779ef31","496927b9d1a6a7bea58453b9749b878ee1040ddc22de6db6e689db11ec111ebb","d17c142d0bbf9f20c696ed0224d1740fbbda0b0211225f2e48732809e15f2505","67e2a7d5facca25f1df14bc4fa4167337405fdc98b47df49b98aa2dcdaff5696","e16f1bf72fcbea703f1b9dba81e6faaf65e29d24128f9e09504c9a862f41c9d1","2e47b85f55b2c6606b137b4be24ea386c58bd9621370d2373e95b530c955102e","1f9fcdb7e24f1f9b391bb9200e32f6353781a1c604159a6257e9d8f1df7b9bee","3c788378c8c8d9525c168ce0578d536607896146b88bb5f6670ab19834be30b3","d16aba532f5391ccb1a61f90cf8e904ecabf3105641c398df0f4a79cac27d472","8d3a30c921fa2ea91d1bffc7fd9b8c42dc74bc819c503208122bd2e84cbffb5f","e4fb93fd2ec72ebaaa1e657435d607ed155da0005e6e30c440257d9fa877d834","ad103c0c76b809a8830ca4f9a8c8cb43b53d578bc63dc8c63b1342f1de90f8d8","7e15e2f23da806eabcf4bd0f1e48d7a5baeface210f05837171b50eed7d2b894","fb5e2afe7c4f988b615085166421f8d88464f6234abcc740ed861eccc0e17ba4","652af46b250ee5144ecb0eef7bcfa1647c88fd170cca974bdff9bb315f349f52","790daabde36636c46a264b1628f488ae49b2b378810383a1059ee722e82ceab8","89a58d0ab59eec78a6b6532e5d748f9942568891619633c890638a2912224ad5","9af24ffe92056dea7acff1dee779be364ad35e5f9861ca417d17bfb447a0a230","8fab0b106acb9de629cc3f7bf784187cd59d506d734917c4f140d02f0dcd167d","d0ba3b6aaef0c96be907938b6fb2a3a04a5db59de34a40f7e426bc7f10bb46d6","d91c919538e393ed3c649270a73f239ea7cd9f312dcee7dad037869a6eb0eee0","fcf5f4ff294643e6ea5100d09f40668a3a8744b73b8f1c397fac4b17ccecc72f","af3bebc2d30fe79abc9a505bc890d16af72f8ea21ec59009e9d57c2d8f6e0b01","784c657f85bebb1a7d94ef05e10f1cad4abdf32798203ef8631f7c3aca2390dd","e488fdf1efc9112b9ae08aaf2be027c3cc5603b916582c45d73bb3885728543f","a43b695758408470608b548841c97ad3827e453fe81ad835e29b9871129785f7","96dd9a7f52627f94b64da26c1ce05d2350941487861c8c27a0014c67273c8a40","0e754d4ed9a6cd6c131515ba94f3f1095fb10ac3cb0c20c2cbeef9e895f924c9","72cac4a4359c6a5e2e5c0ece767455797e46871350324dfe42ab14238f675729","cd091878f6b6994d9307156bda8a4419c7c41c524228d9e830f5fa618d70672e","bd3bbe444bc7cc28757c7669fb186a9ba326d4b65dbf99e18b2b5b9ca66edeb9","b10c73b3d7703d2d870d35631428cdec737d4bbc06706b6fcc6f6e058b8e1594","2543b46883befbefe10c2de9f3a0e7809de7baa09e192edda748443fd15d38d3","01cdf83ab596024078a6ce08ff990770326ceaf16f9081a8e369b9bc5110cadd","bbb9a17f2654caa1d34f49428c0e48ca0fd0d9550f5f82da6f544c924afa17b3","a9d8ba2c15cd99f51aa291034a1afff2f67f1f88259d2162eddaa25e3644032a","affa88c9484982a9aa35b30480059dadfe98c3bbd92f76513ae2d1d7e68096d2","3436c4b40e71c333e253578f6f3176870d4963d5f4ec14862ba5e40794bef8bc","a02f786598d002e2e42854d9bb4fc5a4ac03589538055a0eca03c9ec7ad35457","348863ce75f819f43557d134e5e7ff11a8ae582ac879349cdf9156bb696012f7","235ede03bdeeb87ca21b68fb1398ebc4749a924e2a7219977b71bfc9c51574a7","ec83a163716e8a8c2324e3f6b64c907ba7e5247b43df47f52edef954232c0211","15a76d1390ae38fe474023a51778887a6e39cc4204f65519a448ed7d1931275f","d7a9a81362adcd395f9db48531a89df84461595d189a234796e85ef983399042","7ac3584d37571a5ba61326f50860d843072ea95673e53d23b0b684635db9db00","96354248b7b7fe792c9545b7b153ef20763677692e9baa9aa6d1afbb17376ba7","5dce9f1eea7d40ad9f10295dff47b7de6fbb24b33858c0ff91aa75043e9899d3","d324bb068a3a98f3d7ff92eed388935a5ed4bd46b7678c5bf057b5a2ee9416d3","fe6e76ab5933ca777c6ce422c7023d44799d4832a8c5ce35e3592c8430867329","cc5beca247a7da7286a82c0f3b84686a922d0a402ead5d11b9ddd0dcdef5c762","24661a3d44d16268a8ac8260f35651526c54496ed5f29e559c066b7b7e6776c9","7c7ddd5cfbbab70612c216ab1d1f982468118fead1d57948ed31b41cd692c2fb","461ffe558e162cb3e451654eed59d0090a267818fde655088616be907007d654","829df07ac748fd372b8bcace5b46e6ce0420d2fd65c23f64b86e8a099b69a21b","162ff76724612621b2623b71139fae21981b276595c5e93a909b464c6eaf6310","4ec0abadee52b5287cba7929ce1c34e81a67a046c0299908158497ee85ff20b7","3b60b6d30d8be5b573e75d148abd155fb74cbce0795055965ad505afa4b181f9","5ea0f9746d216da9d45885462fdad43ed26dc4473ac6d289a94661c9a1e7bbbf","96198fa503333a1856039319ad4bc45c6e32afe2ede6a050e23fee2127139a40","e9dbaa3c845b96ebd98a7a3c59296fad4f5cbe4c2e471d0c54a248dab6d575f0","63c5fe7a04273a69125008737aa3c18212a1276b3a2f3892080c346cb589a716","adca096d8a06e8fe1f6f8a1d95dd176e0ec2216f5dce683c9c3656a9bb1e1f10","fe5cfccf2b757c44e7251d6ac822f4892d63f0dbbab920da4f24b893e655e836","e2a5e2a231048f1b0a8c6c123d524adeb3eda1464ac2413fd039cf5afa57bc54","8701130ab14da66b4e908e13c3ece584b420399cc543bafca971c414059ee5e8","811e9e98ddaacadbbcc92015fafd5f5ce0dbebc14f3536cbe225094b1d61f885","f7c8e5f19d7159bde8f8e9c6561c6e517953457faa86018a7f963b72863380fe","c0bc4b28c78bccbb158fb2e8b3e37a86fee5f26b6098a857befd864790da7cd8","e7b604762369c8fa5ffafb6e238a2c7af296e5a25bfa25cb33191b525f064cd0","dcabfb44bd25183c919819f87428fc589b20c3b9586825ec456f94cdb67bd316","41c39405eb8d94777d8b30d2bb295c258391ac4a45deef8d2f569b29bb82938c","f5786f9b0a39c790d245b33436e75576990e41e995e1fff1b0919833a57f4357","970025f12906d26ea3c1c381199eb6702b9c8cb0bec44edc02e86e004cf95eb1","8dafc03ad3ee7acabdb9254c702b80755bdafa7d7548cc6ffc21814e83055abd","e0dba6e973edfd7a5d8a7307ad1e6ec014b51fb7dac507ae132d1f9429016252","cc2f61e4781ad29f2aa93d4850de1b1d8313f242631f10ca17cc99411eb63022","6679676c1dc90d9c371f3de8430bf070ed36d2677d9ce3d2a336b54c5c40c2d7","cf2b168364792895c95f8f98f8fb662f07787e518e6d25b0f7c4aca9927a1bb5","db115e097d9ccc281414e7cedebfb0435d5bb14022f147331fb1bdad09404885","aa78c2b93bce87b73fccd6726cac3cac4f62927460ff3495f2a05553b4c04d3e","298104d50f65103c256ad79ff5128141000e4544a6afaa998d3099bee3975b84","a99f5ced5c95d7603c94c66a4619dfd0e737351bf20757de516b7f8ca193cce9","341b20f291eecbfefa6760a69f7f3f18b2094edcc794f4e78e903a5f0dd86fa6","238dd354909fc4a682e0cc4bd0d1eee8ba03197a4efa3cb284e502355eaec8de","8a3156a33e38b19f00d543a9f7a96054a0a4b051533449fb04267b4e533f55ef","bfd14082a4db87c2847135aab3d617ad7b488b3e65ac82f1620742548ed630f3","e2aa5b5cbc067b485de95616efb852886f4a1a43685ed7ea0ee8e08fec961cb2","3d6d27c275808a7e8540b1778a5d3808542518acda03f5c1ea4c9c5831058ea0","f534c1a02cd756679611ca2b36431b51715a0c59a070d413e292dfa23b9b5c6d","e26cccd0ef5654714877908c1674bee29a8e53d60c8c2d82bdffc12cac6b0fb5","ccf581fb8928f37fbd6509a7d8fa0d32156fc4eb414f434bf81cf7bd6849f7b8","69b227120a5245cddb0805eea82a0bea405872bcf595d2fce9fc03dd16133291","d6f495bfd0020102c67a6f80e411b00b913a001c468001b7cbe8c592c748f301","eb4463ba66be74eb04aeba3bded1f485a6ee90bed8c28a2c2573f0c983834790","a76187885d25a8aed20f71760c116bfb89ed1612d125bc190ab25a1a7a87ed91","76d36099aa1a0c7ea690ee1675ac4dd86cc62e2643cd40c097899268f8d2f7a8","815d7ee4cb5383f94b88688b8f2d70ce3e5df4de147d3669d225f8bfcffad673","afc6a3b94e405b3ae5d5038fe66f3b2412300c93ba1250805ee1a7ad19964ff6","b168bf198df3af94f54863a77ca14dcdb67af689d4cd876328f7c70bbb7b985f","54a40fe6e389146cb444299ef2d7d6e4ec83b05a9df2e7611fd1d1d862b2743a","65ec140c7cde7edee0f611bc84ce505cfa71916571e76f5cc197ba1dcde32f2f","969a96cb343d30bd8a28bad66c77049aeb0fabd9f608ad82caee751082685eeb","b30e161d3bbbe3f8d15b0bc5d9b47d1935ffffc7ced1099fcd84536c906af711","3eaea558e36977f924d85b3207406594568053d7a52950081b7603d112edefce","4918bfaa32bc0f63380d84b19bf5adff3188797b17b055417bbfdb09bd531d1d","5750305060905aff115cc0a6f295357099856fe72d76a1193204c23b4b9417f9","98195f663b1c09572bf794bf2fd7351b05d5895ed471589c0c79ae2e7c7d6b97","355e8226f1a83a02c2c5dc22781defbcf171df314f6f3205318851fd4550132f","8abeb772b7a7763686fd699980b74d9895863a758f2a6b824edb3d5e5c235078","dc1e2e53c9935f62739ca37d9acd484e83fd25bc051e5a330c9be0acbe774253","71a542cb540a3c0d4d954d311937a4df56157b0797489ea5cb8a9e57f449aefe","49fbd0ae0b53936499ca6293450230273cf299e017ac4a1cc8de936d50d8e696","d1a6aec1239a47bbcbbc55324d6ed293f79d4554f6bb0e411e206a9e22c50aa6","4e81f6b7048f1022bd8dd7dc18e43b4aca8967c4ffbfc8fe80bd4277936e5be3","5ff6328b404fb34d2828b501bd16f75bf17590d2b03a66a469a0359da07a06fa","149fbdfda86091cc37779a6eb3f01ac5c73d6e6d34a71e76bb3702a1ebdd8bf6","3c1e92d9a7a0c81d02f016c47de63a39706bb0e36231f2e6727a08cbbef6cfa2","ebec459a7d4933732cb453254997b9b9e7d17319dd40a29946f985719e297927","394dec85f81a33891c71f4e6a1b9a40afdbe93210d5dec749a2791deb57df5e4","19d2786de07b0dd973e5515d84182d2734f1c1ecf602929f75b30081fb20fcce","728d3db3ffdbd649c96fd64cb5766993cc8cb10b4ef207403fb98304eba04f57","8e4ae5371abcf89ca3059e621b666f1a340db0575f0c8635431417528f2d6367","de4f5917a7bf2c62cd3ab4c171620fd6e88a4a92902f02c0808ae67793e6baad","8f09f0abdd346cdbb1e545a44c85dcefeb047d07080628562a328d3e88160beb","4557c1259460570a893f9adb1547b5bdd19948497740c53fbaf654b20ded5855","ddf8a6692f74ae9ebdece687ec1cd9ff63811c5af27381b40c7996053a1b0504","de735626154dab7ceb24b208728b7461aaa5ad8848152ab0b39192e7bc0aa4be","c6296acd69aca14033c815aebc1b4c7fe72b92e44145dfe6432fe855c8c7b463","fe7df3fef3d25c0455e7cd4f36fdff7ad7b4d2163e7285a74d6a98a6cb48a282","7e9bf7c76b60c4402bd48996bfb0d1fa552a576991f9f73dbb856d15b0346793","29199bf01375b374d516e5c8d5d8ce1dac3c07cc52b00eebc0a7ba7b05baeb1c","7696d51d4ce2f2f21e9f73214396bfd823bee6c57f65d39e6a2264c40dd021f2","4b74f4072dacae8ba4f21abf5d042e5da499d027b0aac8e2d8f42f5f591453a8","d3b884fb07719c9457497fa9d5146f7977fed29df303347ff4175f630b903fcf","665320db7cce83346ce47ab3baa657b3115d796d28d8f778a98e7f23962b1247","3f471b5d1f378519b8276a869cb746d5cfc9b2bf4d7e5cbd0bdec3f9b5f81fe7","4afef388856e350489141ad7d90ab0767a02954d53d953f558237e04c89b5d0c","7b16f7f12771f8e25117321d1cb607e06be688ed8db002fe2cb13b716d0662b7","9412339ec64000a6dce5898fd848f025d31ec3b446872070cc041ade876c0c1f","726bc5d2505bf6051e80ede05a576e21d65118c077a8407ef4f9eca8d5445464","05f2af853ef135671b9482077d19a2935e3d924debbcf2f4803110bde114f113","134078b75b0105535f6164680bd73f88f9ddaa84b7e0126aeddd2af7ea27bcb0","264f0b10beaa4141a6bf228d2e22b19ff7baa76b39388da319bffcd1ced741e5","98682512d496bb618315de173d7a25ec363676da2457939f42b75a23c5acab87","1471ca4439a9dc9787976d1c9ca2b913908f4029465c87372d2efe2069c375f0","54b8abfc4713160ce97f91758ee1e8a547ba67c7328177d5e4c40613a3e87f79","60894b9993d7e1a7be37933c9bfe96b228ebc206cada93a44bca9d30e12d8d8f","236ec9d640fd6438a08dd2be3fb739352f147de529b4aa1953e4d4f74d6638b8","e75ea841bf22a156a7ae8f95eb7b1d4479da8c291019148d0a643027356b76c4","4111a7444998538da1f6f76378412547281e30cc5a7249b32e7402f66f83a492","4b9a4b3456012104409fde7f7631be98068daaafe1c49b627b8d92f033960b67","7f7840713032b2ad3bbc379ee2d401489b4e563294f7c87dbaf2424a6682beb2","b52b80732805d494ffee704b80f689772e1db9440c1728b907f7b25b3328d5ea","a805a58a4c72c7d513295fa7102284dd9cf76c1470e1845a6d7e9afa4bafa609","724e0ca25a06f553306e33a45951c368346cf1fc4b26b8bf4bf88b1479131659","b0880e598b7256855af6b9ea2aebdf47372444114264b56da9d25ea5f95d064e","3ff6607fc3c3a85814ec3d6e05e358d99773ee7c7b5e9deed5e086e39f5372a6","62247290540b91ed85258d7e8c67c7786e38bc1111429da9fa42b1b34e4ffdd2","656ee3f8184b5dfb87200f73350e57827dba056130d15064406487c0993f0e6b","b14c19907984b69ce25e011e6391ff6a150bb31f344d643f2a3d5af9aeb4ba73","2b77a0e88653109c708203a50fa23bb50406a9c8c7f61883c92e009f778387d6","2aa50966f709107108e7ad733c129c81f9e42731492948a9c23d4f8a0de5ae1e","080afc7aa193ecf03c78afe2e3d81cc3b18fa482f1bd955b4dca67bcbf220eb1","7282df0d72afd1c283644f5827159ffe0c899850fe121811e6e7e3eb77416868","07c70d4602003ffd9f23f8f6fc2693b7cf024a323a6d6146b11659105ae588fa","79eb7464a0215c82cf6fdc55b2378dc0a3aed416f03dc647fb6956975d446ec1","8cd341d72d1ce25d33dbe1681a9a5f27fecfcf65d426a0d0bb80ce97a1e37d50","4f750b488d0d1019bf8b6651e68689debd6106312ca7f4fca22627fc2d0acc04","4307994ad4d3a8d842a7d7da76f45f84e5eeaf1580e9b6071dd5fa6b8b21de19","87b54711b1c9791dd95b4ff88f814b489089f5b128f29e8a5fb7f6b8123f739e","4763437e8a65ec15310aa20a4ec288eae3de1b94b9426336ac423fddd482d70f","e2398ace0c73a5da036dbd6cab98008a251c709c56f1b665b6202e99ae3450dd","1a59a1ed95ac47cb6d1798a4dee9088b847f41491e57545e788ede35ed1204e5","c89ec75e2ebb2f5c2dce2d5f85ab59951cdd217748a49a6e0bd102fe69f5eb75","e653e5173f22f243892807fcd85800dfa4efe84be40e0ed1cccd15d62e1c9da4","973c290e94835130e51e934d078ab80e975a7bdf5b9563f5fa8de080a06c588c","5abc40134600b35d15fcc7305d5bc9e29942c64dbec6413137b55e77b689da1d","85c3303460c6339a77ec37ed9b7e06974f6d8e1351181b3f6f0c5180e0e7a76f","88d761b9b53ee5aa4ffe94d18e1c05c31ba8778beddcb8418f303c184f4f61f4","8ad1059fc2cf09ab5208fdc50a974a1a0c0f3737d81c2aa0718c583716b49f7b","ded7cab1c0c297958efedb1c4569674117693e0ebb6e8a1366cf7a629ba490c6","997c088bb8c6e1d869a689a3db0157d3efea26fa3fe49ece5f01044321949769","aa4d9968e228a15f4f93ba782c05f19de5aab2598ef8acd819ce72d6f4c9953e","1c4420d12393decf20734fff3f09f44daea5433869633ed11e9fed9b316523ec","25e74c23ca9d123ad4726803694ca249b958270fa3e98eb3de7f339f15241a45","87537c5c41597d3355cd28531f715bcfa77b73f0622d49b28b6064b3c0ba0ab3","56acbddbfb96d3e9502c73d87f216fd16b9954ce7fc345adaa51a054bbd548cd","ea5f8a7e470eec67d9b3a57a311393d9b8146d59e7d3965fc2a07745aabb50d1","d75a15490d5dc9b8bdd209200429a4cd31c139b1503e22e1ef743e6d4fd160f2","fb238a904625a2a0942f8f0aad2c96d5ba7b684b59890599a10d73c1bfd3f771","35c91fef4484772dedb9c253a07ad91912a8e349838bef1e7c85b93b6acd39c8","b92d1f42b729031910871b073bbf05b8d68994a91dc43a7fc64f88abff16db54","beaeb7cae58c1b074e1e5c4c0cc4e205b1763f0e9f413d7062951e1cf539b450","2059b7deab3beb764a2368200ead025358b48fe470bd6850500785d94c8fb5cf","da08b48bf74446b6f988e95f501693844d59d445487d89d2364b8bf431c8a21e","17c4db14970964450f5bdcd1349e6f3035419db7f7f9f88ee966b3b34cbaa8b3","a9e6f34151c8629364892059c1689e2f99775b3642a24854d0330112d6892cff","bf5fb8cea51021d395c45a092c1e97534d498c91812b99fdb07c658cf5990585","4b46f4712ae966996b2cc81949d482063887c55478706e25d942482a44b99b71","71c1350814b0e0683b2725e5eb7d44abf44944d23d5b9ca215cd7e0efe0496e2","93b24ca76698e62732d72800da132367639a4426363c821338bbbd7cf6b64443","ecfa9ce3a5a37d15b813065e8a7cdf677a0f493018e47ce59815443dfbb9c910","83e56d3337e1a6dbafdbe5a2502a84c330b1a328ed2860d689b2ded82b1f5c95","e630f8a3c49d5db0a8af774799abdb8f19675b940a6cfa25eca35e5280709f28","413c03b679ca5bbe6456b8a38ac999fffb3834d0e4a9638bd186533d629ab8cb","ad41c7519202d7daf1ca369d8e85e607e5604474a25e6d2d2e75730922e9ca76","137a44f0f62468569f6cb54ec0ee289b9694bf6acf872c749f20a80bf9e5b991","962133e81ed097721e4b7e9e7dc2d76df05deb5f07f5f2288840b4fe668efd30","a3f113bda985865f0fff6b8ef06cb4a1de6f08cf9925cf5f4e0feb160a627cf7","46398af9458d0dc8f73d41144639f1821073a2f61a51641f77a2d31b55848619","c715c283d860f353983ddf5e4eaea1d7e6131c028307b4a1a7c6ee80c578265d",{"version":"14563e1a6ab8e6ac49018725f12d183486cafc8f83256d9110106f8d2a596f6d","affectsGlobalScope":true},"f394a9d7f647cdb62ccba2d7c62ab4b8690e86c7bd826071e6108f6a9a880875","3b0e07fca438a6c66b48e1f097abf40e3176a2b3355bed940d773c0fdfbe9a0b","ec676b2cb5cbc5532f3070c53eb560ff0564bd9843c688d6d6a488c1dfcdf48e","25488ec0923fa0fd699444ad2456ce5e8b8ae66f4681f3c27da4d15dc26e28b9","2eacb614bc17de46077ea6f39f7dfde557ac86c40a0867eb60d642cc965abb04","6d80e9424591cf47faf00b1cc2e990ec0f596c99d6f47f21acf3ac208c4ff207","2cbfb22a1cbcc21a3ba4119b997db81454f6b0eb600832592dc74bd509739668","3e5c4f3c08c796abed5b58e0aea84931abaad6ff999e79a3851a11a012e5b3a2","c816da1e9aac5af1ff0364a36b6c48b8eb0a5f77ee4acdcb2808a070cae420f0","c584d3c4d3b195d93f5b5ae2e0d636b0426afb3c8f4ebcf13b6b1ec10f43f357","67c763aacd33876afc6a3db9ab77e5e0704d4b5553ce136672de624344f4ecda","09b7e509af380a1d32602acc844227a0586f2291b8ac91a430c9489a2f980094","02595134c799ccbdbe7de2ed806a800e7e371329ab7e2517ebcecdbeea3a7931","f164846baa71658b62da152a6fadbfacc3f2e358c712bd5ef41b3cbb9f56b288","9f6bf4ae00a700facadd7c9e8783f863822b113dd56d5ff0ed9c7febce65d4b7","1e80167581f355b77f1d3683284c36616fcc799b954dcc16933e21def88bcf9a","c49f53e402985abec94ec7d9120dd1754eb4ca082cc387d20b68eee3d7211b80","f8ea0915ad8c57bc41a83617c6ebe48c1e3e67f4acc6b1670820adeff9bc0fd0","589ccb6daa4c713f2bc4a938e01cee8e4e49bb1613ac4e8487d6e0bb31e22aeb","0fbc65becffc51884c48936dec82ff8f0b7a1b739ecba2c27d7ce31aef0d15fb","b383fbf8aaaa46e3a7b14677068026fa40570f6c73cb43560683ce7fce9f84cb","5468406fdc27f582b9ce7c9921ed3b4afb502187c7f69907ccc278a7f1ccbd70","b02b63f7db52fc7ee6468b53136fa64a076e2e4757289c018a8ed563c619261b","5c3038955e6bb4123497a184a5d878b09f5a78460df8685e2c81d30f1bdb7674","2153bfd12033593438d6a0c501f4872ebeeb1a9e5a9b3880fc07dae839f0e258","db957fbcecca7cc57e43b940f8666bb9b7400ce5c4a9fef9744c2d119be481dc","142e997b6952fe35c442f394839761645827cebd4756b4ac806969c329adb625","58850317b3ea1009ba2daefd8db5e8156a33171f2c70c816577f2b978856d98c","a69c4caf5a054cf2aaddf5e6cec18b116deb8ed1415c4e586a384e8a6596268f","410e831d2b8373bc7c71ec5f87a5b56ba8827c133a877aac65df9694b5695084","f63fa4b4d9b9099b44af81928cd7c80ffb318930260ababcbfae04a1f869d9fd","1542fb6a9f509c4647480e13f621d9de8bafe9ae45b54b59121ad4ebf21cdb9a","829d0233ef1650ea7917817da6b4f80aeb2668e87c2f575e9ff01716025032b2","8e4b3a5b64ebb509675722909ab3dc0b87052456825bea90a5306fc1f9e68b17","96a60edef70d230fc1f413e6ad8dbf667ca55dc626603bd1bb2a1e2e7d46e0ab","5953adef9f8020ee8cd1d653d0b4954201ae25627423ef8c0b3700da9d0869ea","ab420b7d6260eed591d2b715562f66cc63b01277cef44e0da8455fb95875ea6b","b531743065bdb99a534c7c769c9e71ac329265f3ed83191fb0182209ae784bce","8cd56e75fd71319b3e708f10d9f2f4a8ee275eb1f854c3c801df4d62e1578403","4a4214aee85054dc65b673f2454348bff78aaf48f13ef71350c152a93038e694","10304779771026da436530224d69c7549f82037e3eac57b8f2565645b3063a05","07bf13c07a20b8228591c71612598a9ea4c973f0c37f95086dc6d4565aa440d5","07d68833fc9535e3422fe5705446294915f3887f7b1086161f037cc031618130","4a6ccfb2053c31045d276deaaf75b230947ab48a07793ae46547c5495f53dbff","fb0861dadea7ef8fd5fc37dbc7d761c59fd092cbe505b03cd901b9770ee1836e","4627626192565165b01f667e5798093fa48cb9103f3f25cc439a06cdab229467","3edd48ad366febf094c15822a7abc9544c2207dfc78cf75ddfda6add10aa4e7f",{"version":"fac25c0e2f59174f4f471d04d2d8ab01a3e695de535a017b5c16a10dc4f2866e","signature":"dd5401f6113912bd8162a157bb0d19be36a0aecf6c67095ef5e2f9964f563c4d"},"dfc3540874a9ea366bac1dde0877a4bc1538df5c453b2a8467d054dae4c30f84",{"version":"625a05dad42eb0e0cd583567a6b6e0fbab23a4e1274b5b0058c33167d85cb80a","signature":"a3ddde8b70134db2f84dc445731843c74892bd3d505cb7f93fad594db115bba1"},{"version":"cac19753cc25ac223d22b7b16333e261eb328b877dc4a2eee9dc73c8cc6e8fd2","signature":"bdf48bf7e837f41bd26c72688ca2bd5b4ee26833535bc7c2f7eca40c1375766c"},{"version":"d7d0c327ed89f294e113bbc46b1527d796c513cdb84a3b0df8c41daf52cb7762","signature":"7b4ec82830ec4a62fe42098f884917ef5ab399a38b284d1e882bf4fbb6397f6b"},"40dd9d69cfdf0d23ff90884f910a975c36151c4206f58d9ac8b4da97f9765283","d2c0bbd94e93bc74ee80066f5d0d1c7fe7fdd7ff3c1041529b998978cadda606","49014fb4afa4d30980b83d422c9064e7ff9a6af09f0aa9aaa00a304cb25ed3e5","3abea28c81680d4f1b7f13c8f048c309af1b74d36654d66f32008852f88126cb","8e96321e94e0bf767e3b621e74b4560256817ba93010f1162a4879558bb7211d","c0960b77393b85216ee6b81dfc02766bfe5c204e2dc23d9584d68ceb23dfb383","80e109f2ece7df0d6b66100f8aa48316f1584a4a7602764baa66e2b63402e1c5","7a53b73908320659a22f031463e6c22573539dcf07a0470961f232b37414e384","9d88473822eff1b1e5cd0303965523692861a2f0742012ec87b834ddbb5cf3ed","4433114f6f31fcefb0381d4c1e75ac4f7d66add893583958c50db25904055578","ac06534c7fbdcfd36e5ae43fcda5dfd07b6aeb75729b07f50e1f368aab697b75","a477914e4e11e779a90dde2a1dddf5fd6c4e256cbde475998cf0af8996a0c947","c9bff56a1925c91534a2abbb8ad21337f8bca10061cf6e32ef90b7590b4ad364","900047558d384c5b86873b38d9c596e88f6a31986e083dbeb97d3a36b4abb000","8b6e9004e08dac7eca384426a9548f898fb73fb55edc5fec0f965c351792be4b","77385c503530ec1feabdd0f5c7486ceed070d8649c36a2417e742ab34c667935","ac127c4c16d20f2fd934a8bb842f54149ff69e2c65b529a82d9b58abc3d11594","72ed87ff855756b118a16f93ab7c7f4f913feb2f672a5dec25f5092a75d08010","58233c9dc2341f1aa3cd7240465c30174a9ae3645526abe9c32bbd73d7be7f89","1d4254e3b839567fa2627fb1d40ecdb86f2e31e39141df13d4207eecaa05d07b","323a178de2931dbcb8167ba82c2fd27d60e83a0297da5af0088c0a710648c193","42dc4d122231873bae981f2e461c3d2a8e715159d71d04f91ae5f8bebdae1a7e","cc5d918d642c292771508d95cb5ee19b70630e6af3f5790ae2e2a933191cd883","880ce868cb7ae08fdfe02d90a7ab8bc14090ba40cebb541a80c3bc8d6ccffd42","5e9f5cb4e69915e7e1fc9b82a2aa8ccbde05a8bcaa41d6832e65f3fb0f4b2d66","6282e89e8fd4e74834ad25af2e3927ea403227cfc44efc52dbd22a7183527824","6beb081cd9584bc2b31e48c7db7c96332753e44009a466de58736185d2edc7da","9d53ad34b89e52d654ebc20625a7467c29113bc469eb8a4aefec4f4e4654fea7","f39aa0aeba66e3bedb2332688913fb693fc93957734803cd7c8b4976b39ccd34","c2489c80994d62e5b51370a6f02f537db4c37af5f914fcb5b2755b81f1906cae","913e12626ff2cb7da9ee1dda2ffb365f1adfc72339d5d64aca4bbb988fc980c8","b5717126992a2f5649478187cb466685e88b2b22e91c7ea240b63ef4ccc3f9ef","1ff19df896119975cc998951fe34d47019e3fd60f0756ee639748d3d466e4ac3","88611953948fa742e4768d513be281db5bb8046d8d40b563daeb69f527c2b520","df0d65967d334572873ab09b3738d97b318daf48e11f38069d1c6af07a160600","a56f7cf088ed57f3fcec373dfee7db9f0fc4ad6ef93d16a492cdfdce997560a4","d531b0b6ff9aaf9b0f0015ea6e18d4b0d82a2cc61afe71287f93ef1c3c46f68c","105be0c01275d5b8899a6d41244efc0e2cc94c31b82c37c2e791b4dacdbf09c0","d723934a74ddf1a04abb1bfea68f96fc2ad7d6d15117a949e58ced2c9b77f046","6fc859e7fce048ccf3733fcfbffc918823e4287651a0c811ecf1b0eb20546d58","d79df5e0b13078320ae0ceb99198fe7e1aba6fca446a4336c2bc7a1662d39a1d","fae1043da49c6d57a7f0fe2190d5c9eca3f90fa6d61d519abcfb55fcdcffd6cc","b50261db23dab701daaeeb8d2b8f92b696edfb047951fad3b6ccc73b25bca925","e5245231f7536c1ab065f42fed8a203801f8ad8a89758d68dbd52ce117b23625","dd952b4462e75d466dd3d2bc5e6362edf6e4203b0339795665d2e87521953cde","33c73ae5d6e79b3cc91ce57f21e924c7451d5fb78ba24929f0a0e7da46d10578","8eebce9498203071c792574d08999be1112d816ec5852dad58c684a7a7018dc5","cc6022e1c3ed0ef206fa510fbb8b843c515800f703f1ee8839f5179e89b806a1","b38dd9bb515fa0ced061d70d64432f503a36198e19892dab9da10d34e3e99bb9","0aef6d495ed0901e42845dbbd6a968f7659d6518d800ab213d074208086401fc","dd7a640dc3e916ff3e384b1c9675c000e3245c21dadf8dc0bb1f903797cfe457","b565c7875a79151d43273d4ed4794a9584fe76855a72a70a9b5666844c78262f","09320cfb9414938879d2afa0358de9a44f7d7ae9704688f7cec5e10a14a7d913","68ed0d3dff7a59cc2f3e9413d532efb27720aeb353bdb7ae793cd80143c5ccaf","2272160e53982cfb63625ca42b0552becc61dcc2adf4a927f9812ee2af4b0d28","0214f8d8031b8baa7bc3e7712c06568a5cd5fe04bb17eb6e8b9db8d9c3e14dc0","6c38bf01505e856814c80c543c809ca5fc6ce23c22504238bd6521e35e95af9f","99d5e54ad805b977a5f847ff186c9cbf6a799c523207c816e0308b59e87e0ad9","3227a60363a244c44920b51efbc4f02f817c9d743c59f869ee7261cbc1cbe043","69049f28225687c6804ad624118c9be713ab55d2be4594563660d7f734aa8db7","b668b7fb7c52a05fb9233a27ba5099a73cd8e157b037d67399336635495ab483","b25c5f2970d06c729f464c0aeaa64b1a5b5f1355aa93554bb5f9c199b8624b1e","069733dc220affbe58d9c1d1a93af3707bc515aaed761701d8741b57da4cb964","3051751533eee92572241b3cef28333212401408c4e7aa21718714b793c0f4ed","691aea9772797ca98334eb743e7686e29325b02c6931391bcee4cc7bf27a9f3b","6f1d39d26959517da3bd105c552eded4c34702705c64d75b03f54d864b6e41c2","6d829824ead8999f87b6df21200df3c6150391b894b4e80662caa462bd48d073","6a9c5127096b35264eb7cd21b2417bfc1d42cceca9ba4ce2bb0c3410b7816042","78828b06c0d3b586954015e9ebde5480b009e166c71244763bda328ec0920f41","16d51f964ec125ad2024cf03f0af444b3bc3ec3614d9345cc54d09bab45c9a4c","ba601641fac98c229ccd4a303f747de376d761babb33229bb7153bed9356c9cc",{"version":"44f372b501e58c4a02bcdf4772d25a1239abd89339a19e7c25527aa0cbf37c32","affectsGlobalScope":true},"3ebae8c00411116a66fca65b08228ea0cf0b72724701f9b854442100aab55aba","cddf5c26907c0b8378bc05543161c11637b830da9fadf59e02a11e675d11e180","3d2cd8f3047fff04a71e7037a6a4cb9f4accb28dbd8c0d83164d414811025af0","de18acda71730bac52f4b256ce7511bb56cc21f6f114c59c46782eff2f632857","7eb06594824ada538b1d8b48c3925a83e7db792f47a081a62cf3e5c4e23cf0ee","905c3e8f7ddaa6c391b60c05b2f4c3931d7127ad717a080359db3df510b7bdab","d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322","69da61a7b5093dac77fa3bec8be95dcf9a74c95a0e9161edb98bb24e30e439d2","561eca7a381b96d6ccac6e4061e6d2ae53f5bc44203f3fd9f5b26864c32ae6e9","62ea38627e3ebab429f7616812a9394d327c2bc271003dfba985de9b4137369f","b4439890c168d646357928431100daac5cbdee1d345a34e6bf6eca9f3abe22bc",{"version":"ce2169125f42515a26fa60977b6d56ae407f3462e28832fbf6a9013f6a828bab","affectsGlobalScope":true},"0359682c54e487c4cab2b53b2b4d35cc8dea4d9914bc6abcdb5701f8b8e745a4","96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","675e702f2032766a91eeadee64f51014c64688525da99dccd8178f0c599f13a8","378df8bbbb9e3f6fca05d58f644aab538e1062eab5e778fb0b83d41125df246d","d88a479cccf787b4aa82362150fbeba5211a32dbfafa7b92ba6995ecaf9a1a89","187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","c24ad9be9adf28f0927e3d9d9e9cec1c677022356f241ccbbfb97bfe8fb3d1a1","0ec0998e2d085e8ea54266f547976ae152c9dd6cdb9ac4d8a520a230f5ebae84","9364c7566b0be2f7b70ff5285eb34686f83ccb01bda529b82d23b2a844653bfb","00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","82251920b05f30981c9a4109cb5f3169dce4b477effc845c6d781044a30e7672","3c92b6dfd43cc1c2485d9eba5ff0b74a19bb8725b692773ef1d66dac48cda4bd","3e59f00ab03c33717b3130066d4debb272da90eeded4935ff0604c2bc25a5cae","9fa6b83a35e897f340858995ca5d77e901d89fd18644cd4c9e8a4afe0b2e6363",{"version":"0714e2046df66c0e93c3330d30dbc0565b3e8cd3ee302cf99e4ede6220e5fec8","affectsGlobalScope":true},"2a2e2c6463bcf3c59f31bc9ab4b6ef963bbf7dffb049cd017e2c1834e3adca63","f313731860257325f13351575f381fef333d4dfe30daf5a2e72f894208feea08","951b37f7d86f6012f09e6b35f1de57c69d75f16908cb0adaa56b93675ea0b853","3816fc03ffd9cbd1a7a3362a264756a4a1d547caabea50ca68303046be40e376","0c417b4ec46b88fb62a43ec00204700b560d01eb5677c7faa8ecd34610f096a8","13d29cdeb64e8496424edf42749bbb47de5e42d201cf958911a4638cbcffbd3f","c9ad058b2cc9ce6dc2ed92960d6d009e8c04bef46d3f5312283debca6869f613","2b8264b2fefd7367e0f20e2c04eed5d3038831fe00f5efbc110ff0131aab899b","08b428a44bc98005536a12456518797e9afe2a08e8b5d9785641713a54475881","3169db033165677f1d414baf0c82ba27801089ca1b66d97af464512a47df31b5","c6c4fea9acc55d5e38ff2b70d57ab0b5cdbd08f8bc5d7a226e322cea128c5b57","bc81aff061c53a7140270555f4b22da4ecfe8601e8027cf5aa175fbdc7927c31","3bdd93ec24853e61bfa4c63ebaa425ff3e474156e87a47d90122e1d8cc717c1f","5a2a25feca554a8f289ed62114771b8c63d89f2b58325e2f8b7043e4e0160d11"],"options":{"esModuleInterop":true,"jsx":1,"module":99,"skipLibCheck":true,"strict":false,"strictNullChecks":true,"target":2},"fileIdsList":[[137,140,142,194,195,198,251,280],[137,142,194,195,198,251,280],[137,140,142,145,156,159,181,184,194,195,198,251,280],[137,140,142,145,156,159,181,182,186,194,195,198,251,280],[137,140,142,145,156,159,181,194,195,198,251,280,304,307],[137,140,142,156,159,181,188,189,190,191,194,195,198,251,280],[137,140,142,156,159,181,194,195,198,251,276,280,290],[137,140,142,155,159,181,194,195,198,251,280],[137,140,142,145,156,159,181,194,195,198,251,280,293],[137,140,142,145,156,159,181,194,195,198,251,280,295],[137,140,142,145,156,159,181,194,195,198,251,280,297],[137,140,142,146,156,159,181,194,195,198,251,280,299],[137,140,142,145,156,159,181,194,195,198,251,280,301],[137,140,142,145,156,159,181,191,194,195,198,251,280],[137,140,142,194,195,198,251,280,434],[97,137,140,142,194,195,198,251,280,436],[97,137,140,142,145,156,159,180,185,194,195,198,251,280,329,332,437,669,670],[97,137,140,142,158,159,194,195,198,251,280,304,328,332,917,942],[97,137,140,142,156,194,195,198,251,280,304,308,329,332,436,437,973],[137,140,142,194,195,198,251,280,329,997,998],[97,137,140,142,194,195,198,251,280,317,329,686,917,926],[137,140,142,194,195,198,251,280,436,686,917],[137,140,142,159,194,195,198,251,280,292,311,329,437],[97,137,140,142,145,156,159,180,194,195,198,251,280,296,329,332,437,669],[137,140,142,194,195,196,198,251,280],[137,140,142,194,195,198,251,280,971,972],[137,140,142,194,195,198,251,280,686,917,971,972],[137,140,142,194,195,198,251,280,926,931],[97,137,140,142,145,156,159,194,195,198,251,280,294,329,332,434,437,917],[97,137,140,142,145,156,159,180,194,195,198,251,280,298,329,332,434,437,917,1002],[97,137,140,142,145,159,194,195,198,251,269,272,275,280,311,329,668,1000,1002,1003],[97,137,140,142,156,194,195,198,251,280,329,332,670,686,917,1004],[137,140,142,147,159,182,183,194,195,198,231,234,251,275,280],[137,140,142,159,194,195,198,251,267,280],[137,139,140,142,159,194,195,198,231,251,275,280],[137,140,142,146,159,194,195,198,231,251,275,280],[137,140,142,159,194,195,197,198,235,251,268,269,270,271,280],[97,137,140,142,194,195,198,251,280,926,931],[97,137,140,142,194,195,198,251,280],[97,137,140,142,145,156,159,180,183,192,194,195,198,231,251,280,309,312,329,330,332,333,338,410,411,413,437,668,942,973,976,977,978],[97,137,140,142,194,195,198,231,251,274,280,309,328,332,668,942,976],[97,137,140,142,145,159,180,194,195,198,231,251,274,280,304,309,311,320,329,332,338,917,942,976],[97,137,140,142,194,195,198,251,280,309,332,333,668,976,979,981,982,983],[137,140,142,194,195,198,251,280,980,982],[137,140,142,145,159,194,195,198,251,280],[97,137,140,142,194,195,198,231,251,280,309,332],[137,140,142,194,195,198,251,280,668],[137,140,142,194,195,198,251,280,338,927,935],[97,137,140,142,179,194,195,198,251,280,668,917],[97,137,140,142,194,195,198,251,280,339,917,935],[137,140,142,159,194,195,198,251,280,332,338,435,917,926,935,941,943,944,946],[97,137,140,142,159,194,195,198,251,280,686,917,926,948],[137,140,142,159,194,195,198,251,280,329,337],[137,140,142,159,194,195,198,251,280,332,668,942,945],[97,137,140,142,194,195,198,251,280,332,338,668,917,935,950],[97,137,140,142,194,195,198,251,280,917],[97,137,140,142,145,156,159,180,194,195,198,251,280,303,329,332,437,669,670],[97,137,140,142,194,195,198,251,280,926,931,968],[137,140,142,146,159,181,194,195,196,198,231,234,251,271,275,280,295,301,321,329,332,346,349,374,437],[97,137,140,142,194,195,198,251,280,433],[97,137,140,142,157,194,195,198,230,231,251,274,280,985],[97,137,140,142,146,157,194,195,198,231,251,272,274,275,280,310,311,329,668,942,985],[97,137,140,142,146,194,195,198,251,272,280,310,311,329,333,434,437,917,942,985,986],[97,137,140,142,194,195,198,251,280,310,329,330,332,338,434,917,985,988,989,990,992,993],[97,137,140,142,194,195,198,251,275,280,333,987],[97,137,140,142,194,195,198,251,272,280,310,437,917,942,985,986],[97,137,140,142,157,194,195,198,230,231,251,274,280,333],[97,137,140,142,156,157,180,194,195,198,231,251,274,275,280,291,329,330,332,338,386,409,412,434,437,939,973,991,995],[97,137,140,142,145,156,157,159,180,183,192,194,195,198,230,231,251,272,274,275,280,310,312,329,330,332,333,338,386,410,413,434,437,917,942,973,977,978,985,986,991],[97,137,140,142,145,156,159,182,187,194,195,198,251,280,304,308,329,332,434,436,437,669,973],[97,137,140,142,158,159,194,195,198,251,280],[97,137,140,142,156,159,182,194,195,198,251,280,295,302,321,329,332,335,437,671,929,953,974],[137,140,142,159,194,195,198,251,275,280,312,329,332],[137,140,142,145,159,194,195,198,251,280,334],[97,137,140,142,194,195,198,251,280,329,330],[97,137,140,142,158,194,195,198,251,280],[137,140,142,194,195,198,251,275,280,1009],[137,140,142,194,195,198,251,280,329,330],[137,140,142,159,194,195,198,251,280,331,332,942],[137,140,142,194,195,198,251,280,324,330],[97,137,140,142,194,195,198,251,280,327,328,329,330,331],[137,140,142,194,195,198,251,280,332],[97,137,140,142,194,195,198,251,280,330,332,336,338],[97,137,140,142,180,194,195,198,251,280,317,329,1007,1011],[137,140,142,146,157,158,194,195,198,251,280],[137,140,142,145,156,159,183,194,195,198,232,233,251,280],[137,140,142,146,194,195,198,251,280],[137,140,142,156,194,195,198,251,280,304,306],[137,140,142,194,195,198,251,280,304],[137,140,142,194,195,198,232,251,280,304,305],[137,140,142,147,156,194,195,198,251,280,304,305,306],[137,140,142,144,145,147,148,149,155,194,195,198,251,280],[137,140,142,145,147,148,194,195,198,251,280],[137,140,142,147,159,182,183,194,195,198,251,280],[137,140,142,146,147,159,182,183,194,195,198,251,280],[137,140,142,159,194,195,198,251,280],[137,140,142,145,149,159,194,195,198,233,234,251,280,322,323,325,326],[137,140,142,147,148,149,159,182,194,195,198,251,280],[137,140,142,145,146,157,159,194,195,198,251,280],[137,140,142,147,156,159,182,183,194,195,198,251,280],[137,140,142,147,182,183,194,195,198,251,280],[137,140,142,146,147,149,159,182,183,194,195,198,251,280],[137,140,142,155,194,195,198,251,280,289],[137,140,142,147,156,159,182,183,194,195,198,251,275,280],[135,136,137,140,142,194,195,198,251,280],[137,140,142,194,195,198,251,280,1039],[137,140,142,194,195,198,251,280,1044],[137,140,142,146,194,195,198,251,257,280],[92,137,140,142,146,194,195,198,243,250,251,252,253,254,255,256,257,259,260,280],[92,137,140,142,194,195,198,251,280],[92,137,140,142,146,194,195,198,251,280],[137,140,142,194,195,198,236,237,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,280],[137,140,142,146,194,195,198,243,251,252,259,267,280],[92,137,139,140,142,146,194,195,198,251,252,253,258,280],[137,140,142,146,194,195,198,243,250,251,252,254,255,257,258,259,267,280],[137,140,142,146,194,195,198,251,252,254,257,259,280],[137,140,142,146,194,195,198,251,252,257,260,267,280],[137,140,142,146,194,195,198,251,267,280],[137,140,142,146,194,195,198,251,252,253,255,256,267,280],[137,140,142,146,194,195,198,251,252,255,259,280],[92,137,140,142,146,194,195,198,250,251,252,255,260,280],[49,92,137,140,142,194,195,198,280],[137,140,142,194,195,198,251,280,918],[97,137,140,142,194,195,198,251,280,919,920,921,922,923],[97,137,140,142,194,195,198,251,280,920,924],[97,137,140,142,194,195,198,251,280,922,924],[94,137,140,142,194,195,198,251,280,918],[137,140,142,194,195,198,251,280,924,925],[97,137,140,142,194,195,198,251,280,672],[97,137,140,142,194,195,198,251,280,672,673,674],[97,137,140,142,194,195,198,251,280,672,674,682],[97,137,140,142,194,195,198,251,280,672,673],[137,140,142,194,195,198,251,280,675,676,677,678,679,680,681,683,684,685],[137,140,142,194,195,198,251,280,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,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667],[137,140,142,194,195,198,251,280,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,908,909,910,911,912,913,914,915,916],[137,140,142,194,195,198,251,280,430],[137,140,142,194,195,198,251,280,424,426],[137,140,142,194,195,198,251,280,414,424,425,427,428,429],[137,140,142,194,195,198,251,280,424],[137,140,142,194,195,198,251,280,414,424],[137,140,142,194,195,198,251,280,415,416,417,418,419,420,421,422,423],[137,140,142,194,195,198,251,280,415,419,420,423,424,427],[137,140,142,194,195,198,251,280,415,416,417,418,419,420,421,422,423,424,425,427,428],[137,140,142,194,195,198,251,280,414,415,416,417,418,419,420,421,422,423],[92,137,140,142,194,195,198,200,251,280],[92,137,140,142,194,195,198,200,204,251,280],[92,137,140,142,194,195,198,200,201,202,203,205,206,207,251,280],[137,140,142,146,194,195,198,199,200,201,204,208,209,212,213,221,228,229,251,280],[137,140,142,150,194,195,198,200,212,251,280],[137,140,142,194,195,198,200,213,251,280],[137,140,142,194,195,198,208,251,280],[137,140,142,194,195,198,199,200,208,213,228,251,280],[92,137,140,142,150,194,195,198,199,200,208,213,251,280],[137,140,142,194,195,198,199,200,208,222,223,224,225,226,227,251,280],[92,137,140,142,194,195,198,200,212,251,280],[137,140,142,194,195,198,199,200,224,251,280],[137,140,142,194,195,198,199,200,208,224,251,280],[137,140,142,150,194,195,198,199,200,208,228,251,280],[137,140,142,194,195,198,200,223,251,280],[137,140,142,194,195,198,215,216,217,218,251,280],[137,140,142,194,195,198,210,211,214,219,220,251,280],[92,137,140,142,194,195,198,213,251,280],[49,92,137,140,142,194,195,251,280],[92,137,140,142,194,195,198,251,280,287],[92,137,140,142,146,194,195,198,251,278,280,281,282,288],[137,139,140,142,194,195,198,251,280,289],[49,137,139,140,142,146,194,195,198,251],[49,92,137,140,142,194,195,198,251,280],[137,140,142,194,195,198,251,280,283,284,285,286],[137,140,142,194,195,198,251,280,283,286],[137,140,142,194,195,198,251,280,283,284,285],[137,140,142,194,195,198,251,280,283],[137,140,142,194,195,198,239,240,242,243,245,246,247,248,249,251,280],[137,140,142,194,195,198,244,251,280],[137,140,142,146,194,195,198,243,248,251,280],[92,137,140,142,146,194,195,198,241,242,251,280],[49,137,139,140,142,146,194,198,251,280],[92,137,140,142,195,198,251,280],[137,140,142,150,151,194,195,198,251,280],[137,140,142,151,152,153,154,194,195,198,251,280],[137,140,142,152,194,195,198,251,280],[137,140,142,155,194,195,198,251,280],[137,140,142,194,195,198,251,280,313],[137,140,142,194,195,198,251,280,315],[49,92,137,140,194,195,198,251,280],[137,140,142,194,195,198,251,280,356],[137,140,142,194,195,198,251,280,353,354,355,356,357,360,361,362,363,364,365,366,367,368,369,370],[137,140,142,194,195,198,251,280,352],[137,140,142,194,195,198,251,280,359],[137,140,142,194,195,198,251,280,353,354,355],[137,140,142,194,195,198,251,280,353,354],[137,140,142,194,195,198,251,280,356,357,359],[137,140,142,194,195,198,251,280,354],[137,140,142,194,195,198,251,280,371],[97,137,140,142,194,195,198,251,280,432],[137,140,142,194,195,198,251,280,1044,1045,1046,1047,1048],[137,140,142,194,195,198,251,280,1044,1046],[61,92,137,140,142,194,195,198,251,280],[137,140,142,194,195,198,251,280,1051],[58,61,92,137,140,142,194,195,198,251,280,1053,1054],[59,92,137,140,142,194,195,198,251,280],[137,140,142,194,195,198,251,280,1057],[137,140,142,194,195,198,251,280,1059],[137,140,142,194,195,198,251,280,1060],[137,140,142,194,195,198,251,280,359,1066],[137,140,142,194,195,198,251,280,1070,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082],[137,140,142,194,195,198,251,280,1070,1071,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082],[137,140,142,194,195,198,251,280,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082],[137,140,142,194,195,198,251,280,1070,1071,1072,1074,1075,1076,1077,1078,1079,1080,1081,1082],[137,140,142,194,195,198,251,280,1070,1071,1072,1073,1075,1076,1077,1078,1079,1080,1081,1082],[137,140,142,194,195,198,251,280,1070,1071,1072,1073,1074,1076,1077,1078,1079,1080,1081,1082],[137,140,142,194,195,198,251,280,1070,1071,1072,1073,1074,1075,1077,1078,1079,1080,1081,1082],[137,140,142,194,195,198,251,280,1070,1071,1072,1073,1074,1075,1076,1078,1079,1080,1081,1082],[137,140,142,194,195,198,251,280,1070,1071,1072,1073,1074,1075,1076,1077,1079,1080,1081,1082],[137,140,142,194,195,198,251,280,1070,1071,1072,1073,1074,1075,1076,1077,1078,1080,1081,1082],[137,140,142,194,195,198,251,280,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1081,1082],[137,140,142,194,195,198,251,280,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1082],[137,140,142,194,195,198,251,280,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081],[137,140,142,194,195,198,251,280,1088],[137,140,142,194,195,198,251,280,1084,1085,1086,1087],[90,137,140,142,194,195,198,251,280],[48,137,140,142,194,195,198,251,280],[89,90,137,140,142,194,195,198,251,280],[49,137,140,142,194,195,198,251,280],[50,58,59,66,75,137,140,142,194,195,198,251,280],[50,51,58,66,137,140,142,194,195,198,251,280],[82,137,140,142,194,195,198,251,280],[53,54,59,67,137,140,142,194,195,198,251,280],[54,75,137,140,142,194,195,198,251,280],[55,56,58,66,137,140,142,194,195,198,251,280],[56,137,140,142,194,195,198,251,280],[57,58,137,140,142,194,195,198,251,280],[58,137,140,142,194,195,198,251,280],[58,59,60,75,81,137,140,142,194,195,198,251,280],[59,60,137,140,142,194,195,198,251,280],[61,66,75,81,137,140,142,194,195,198,251,280],[58,59,61,62,66,75,78,81,137,140,142,194,195,198,251,280],[61,63,78,81,137,140,142,194,195,198,251,280],[91,137,140,142,194,195,198,251,280],[58,64,137,140,142,194,195,198,251,280],[65,81,137,140,142,194,195,198,251,280],[56,58,66,75,137,140,142,194,195,198,251,280],[67,137,140,142,194,195,198,251,280],[68,137,140,142,194,195,198,251,280],[48,69,137,140,142,194,195,198,251,280],[80,137,140,142,194,195,198,251,280],[71,137,140,142,194,195,198,251,280],[72,137,140,142,194,195,198,251,280],[58,73,137,140,142,194,195,198,251,280],[73,74,82,84,137,140,142,194,195,198,251,280],[58,75,137,140,142,194,195,198,251,280],[76,137,140,142,194,195,198,251,280],[77,137,140,142,194,195,198,251,280],[66,78,137,140,142,194,195,198,251,280],[79,137,140,142,194,195,198,251,280],[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,137,140,142,194,195,198,251,280],[66,80,137,140,142,194,195,198,251,280],[72,81,137,140,142,194,195,198,251,280],[75,83,137,140,142,194,195,198,251,280],[84,137,140,142,194,195,198,251,280],[88,137,140,142,194,195,198,251,280],[58,60,75,81,84,85,137,140,142,194,195,198,251,280],[75,86,137,140,142,194,195,198,251,280],[93,94,95,96,137,140,142,194,195,198,251,280],[58,61,63,66,75,78,81,86,92,137,140,142,194,195,198,251,280],[137,140,142,194,195,198,251,280,1095],[137,140,142,173,194,195,198,251,280],[137,140,142,173,174,175,176,177,194,195,198,251,280],[137,140,142,162,163,164,165,166,167,168,169,170,171,172,194,195,198,251,280],[137,140,142,194,195,198,251,280,1062,1063],[137,140,142,194,195,198,251,280,1062,1063,1064,1065],[115,137,140,142,194,195,198,251,280],[100,101,137,140,142,194,195,198,251,280],[100,137,140,142,194,195,198,251,280],[81,92,97,137,140,142,194,195,198,251,280],[97,107,137,140,142,194,195,198,251,280],[97,106,137,140,142,194,195,198,251,280],[109,113,137,140,142,194,195,198,251,280],[97,110,114,137,140,142,194,195,198,251,280],[104,137,140,142,194,195,198,251,280],[61,92,114,135,137,140,142,194,195,198,251,280],[100,101,102,137,140,142,194,195,198,251,280],[103,137,140,142,194,195,198,251,280],[97,99,114,135,137,140,142,194,195,198,251,280],[61,72,81,92,101,103,104,115,116,122,123,124,125,126,127,128,129,132,137,140,142,194,195,198,251,280],[61,81,92,133,137,140,142,194,195,198,251,280],[61,72,81,92,137,140,142,194,195,198,251,280],[61,72,92,101,124,137,140,142,194,195,198,251,280],[72,81,92,97,104,108,110,111,112,114,137,140,142,194,195,198,251,280],[117,137,140,142,194,195,198,251,280],[117,118,119,120,121,137,140,142,194,195,198,251,280],[103,130,131,137,140,142,194,195,198,251,280],[72,92,137,140,142,194,195,198,251,280],[61,72,81,92,97,99,104,105,113,135,137,140,142,194,195,198,251,280],[137,140,142,194,195,198,251,280,1013],[140,142,194,195,198,251,280],[137,140,142,194,195,198,251,280,934],[110,137,140,142,194,195,198,251,280],[61,72,92,97,98,104,114,115,134,137,140,142,194,195,198,251,280,931],[137,140,142,194,195,198,251,280,358],[97,137,140,142,194,195,198,251,280,959],[97,137,140,142,194,195,198,251,280,960,961],[137,140,142,194,195,198,251,280,959,960,962],[97,137,140,142,194,195,198,251,280,966],[97,137,140,142,194,195,198,251,280,954],[97,137,140,142,194,195,198,251,280,955,956,957,958,967],[97,137,140,142,194,195,198,251,280,954,955],[97,137,140,142,194,195,198,251,280,964,965],[97,137,140,142,194,195,198,251,280,963,964],[97,137,140,142,194,195,198,251,280,963],[97,135,137,140,142,194,195,198,251,280,931],[137,140,142,194,195,198,251,280,378,380,381,382,383,384],[137,140,142,194,195,198,251,280,378,379],[137,140,142,194,195,198,251,280,380],[137,140,142,194,195,198,251,280,379,380],[137,140,142,194,195,198,251,280,378,380],[137,140,142,194,195,198,251,280,431],[97,98,135,137,140,142,194,195,198,251,280],[137,140,142,194,195,198,251,280,389,393,395,396,397,398],[137,140,142,194,195,198,251,280,393,395,397,398],[137,140,142,194,195,198,251,280,389,397],[137,140,142,194,195,198,251,280,389,393,395,397,398],[137,140,142,194,195,198,251,280,389,391,392,393,395,396,397,399,400,401,402,403,404,405,406,407,408],[137,140,142,194,195,198,251,280,390,393,395,397],[137,140,142,194,195,198,251,280,397],[137,140,142,194,195,198,251,280,395],[137,140,142,194,195,198,251,280,389,390,392,393,394,397],[137,140,142,194,195,198,251,280,398],[137,140,142,194,195,198,251,280,395,396],[137,140,142,194,195,198,251,280,389,391,395,397],[137,140,142,194,195,198,251,280,389],[137,140,142,160,194,195,198,251,280],[137,140,142,194,195,198,251,280,332,339,375,936,937,938,1006,1012,1014,1015],[135,137,140,142,194,195,198,251,280],[97,137,140,142,159,194,195,198,251,280,329,332,947,949,951,970,984,994,1018],[97,137,140,142,194,195,198,251,280,332,338,374,435,668,670,935,941,943,944,946,970,975,999,1005],[97,137,140,142,180,183,194,195,198,251,280,292,311,312,329,436,437,669],[97,137,140,142,145,159,194,195,198,251,269,275,280,1001],[97,137,140,142,194,195,198,251,280,312,1023],[97,137,140,142,145,159,183,194,195,198,251,275,280,312,329,333,409,413,973,977,1021,1029],[97,137,140,142,145,159,194,195,198,251,275,280,312,333,409,413,1021,1029],[97,137,140,142,145,156,159,180,194,195,198,251,280,294,329,332,437,669],[97,137,140,142,145,159,183,194,195,198,231,251,272,275,280,312,329,332,333,386,410,411,413,942,973,991,1021,1029],[97,137,140,142,145,159,183,194,195,198,251,275,280,312,329,332,333,377,409,410,412,413,973,991,1021,1029],[97,137,140,142,145,159,194,195,198,231,251,272,275,280,312,329,332,333,386,410,413,942,973,991,1021,1029],[97,137,140,142,145,159,194,195,198,251,280,940],[97,137,140,142,194,195,198,251,280,332,338,668,935],[97,137,140,142,194,195,198,251,280,948],[97,137,140,142,145,156,159,180,183,192,194,195,198,231,251,280,312,329,330,332,333,338,409,412,437,668,935,970,973,977,978,1001,1022,1024,1025,1026,1027,1028],[97,137,140,142,194,195,198,251,280,994,996],[97,137,140,142,194,195,198,251,280,330],[97,137,140,142,194,195,198,251,280,327,329,330,338,436,668,935],[97,137,140,142,194,195,198,251,280,327,329,1033],[97,137,140,142,146,149,156,159,180,194,195,198,231,251,272,275,280,300,329,338,409,412,437,668,935,973],[137,140,142,145,148,156,159,183,194,195,196,198,251,280],[137,140,142,145,159,194,195,198,251,273,280,319,325,327],[137,140,142,161,194,195,198,251,280,309],[137,140,142,161,178,194,195,198,251,280],[137,140,142,161,180,194,195,198,251,274,275,280,310],[137,140,142,145,155,159,161,178,194,195,198,233,234,251,274,275,280,304,317,318,319,320,321,325,327,328],[97,137,140,142,194,195,198,251,280,373,1017],[137,140,142,194,195,198,251,280,372],[137,140,142,194,195,198,251,275,280],[137,140,142,157,194,195,198,230,251,280],[137,140,142,194,195,198,251,275,280,385,386],[137,140,142,194,195,198,251,280,385,386],[137,140,142,194,195,198,251,280,385],[137,140,142,155,181,194,195,198,251,280,325,410],[137,140,142,146,194,195,198,251,275,280],[137,140,142,147,194,195,198,251,280],[137,140,142,143,194,195,198,251,280,324],[137,140,142,146,158,194,195,198,251,280],[137,140,142,159,194,195,198,230,231,251,280],[137,140,142,155,183,194,195,198,231,251,275,280,312,325,411,412],[137,140,142,179,194,195,198,251,280],[137,140,142,180,194,195,198,251,280],[137,140,142,194,195,198,251,273,280],[137,140,142,145,159,194,195,196,198,231,251,272,274,280],[137,140,142,145,156,159,194,195,198,251,275,280],[137,140,142,194,195,198,230,231,251,275,280,325,385,386,388,409],[137,140,142,194,195,198,251,280,314,316],[145,159,309],[142,155,194,198,251,275,312,325]],"referencedMap":[[139,1],[140,2],[143,1],[185,3],[187,4],[308,5],[192,6],[291,7],[292,8],[294,9],[296,10],[298,11],[300,12],[302,13],[303,14],[435,15],[437,16],[671,17],[997,18],[998,19],[999,20],[927,21],[928,22],[1000,23],[311,1],[929,24],[930,1],[950,1],[972,25],[973,26],[1001,27],[971,28],[977,26],[1002,29],[1003,30],[1004,31],[1005,32],[197,1],[235,33],[268,34],[269,1],[270,35],[271,36],[272,37],[932,38],[933,38],[436,39],[979,40],[980,41],[981,42],[984,43],[983,44],[309,45],[982,46],[669,47],[936,48],[937,49],[938,1],[939,50],[940,39],[947,51],[949,52],[941,53],[946,54],[951,55],[952,56],[953,57],[969,58],[948,39],[970,59],[434,60],[986,61],[987,62],[988,63],[994,64],[989,65],[993,66],[990,67],[996,68],[992,69],[310,1],[974,70],[945,71],[975,72],[944,1],[333,73],[335,74],[1006,75],[1007,39],[1008,39],[334,76],[1010,77],[1011,39],[336,39],[670,78],[943,79],[338,80],[332,81],[337,82],[339,83],[1012,84],[1038,1],[159,85],[234,86],[304,87],[320,88],[305,89],[306,90],[307,91],[145,1],[156,92],[144,1],[232,93],[340,94],[341,95],[321,1],[342,1],[182,96],[327,97],[149,1],[183,98],[324,1],[331,99],[189,100],[184,101],[186,100],[343,94],[344,94],[345,94],[188,100],[299,102],[290,103],[276,104],[346,94],[293,94],[295,101],[297,101],[190,100],[301,101],[347,101],[348,101],[191,101],[349,94],[138,105],[1040,106],[1046,107],[1044,1],[256,108],[262,109],[263,110],[252,111],[236,1],[267,112],[264,113],[259,114],[237,1],[260,115],[255,116],[258,117],[261,118],[257,119],[254,120],[265,1],[253,1],[266,121],[251,122],[919,123],[922,39],[924,124],[921,125],[923,126],[920,127],[925,125],[926,128],[918,1],[674,129],[675,130],[676,129],[677,129],[682,129],[678,129],[679,129],[680,129],[681,129],[683,131],[684,131],[685,132],[686,133],[672,39],[673,129],[438,39],[439,39],[440,39],[441,39],[442,39],[443,39],[444,39],[445,39],[446,39],[447,39],[448,39],[449,39],[450,39],[451,39],[452,39],[458,39],[453,39],[454,39],[455,39],[456,39],[457,39],[459,39],[460,39],[461,39],[462,39],[463,39],[464,39],[466,39],[467,39],[465,39],[468,39],[469,39],[470,39],[471,39],[472,39],[473,39],[474,39],[475,39],[476,39],[477,39],[478,39],[479,39],[480,39],[481,39],[482,39],[483,39],[484,39],[485,39],[486,39],[487,39],[488,39],[489,39],[490,39],[491,39],[492,39],[494,39],[493,39],[495,39],[496,39],[498,39],[497,39],[499,39],[500,39],[501,39],[502,39],[503,39],[505,39],[504,39],[506,39],[507,39],[508,39],[509,39],[510,39],[511,39],[512,39],[513,39],[514,39],[515,39],[516,39],[517,39],[518,39],[519,39],[524,39],[520,39],[521,39],[522,39],[523,39],[525,39],[526,39],[527,39],[528,39],[529,39],[530,39],[531,39],[532,39],[533,39],[534,39],[536,39],[535,39],[537,39],[538,39],[539,39],[540,39],[541,39],[542,39],[543,39],[544,39],[547,39],[545,39],[546,39],[548,39],[549,39],[550,39],[551,39],[552,39],[553,39],[554,39],[555,39],[557,39],[556,39],[668,134],[558,39],[559,39],[560,39],[561,39],[562,39],[563,39],[564,39],[565,39],[566,39],[567,39],[568,39],[570,39],[569,39],[571,39],[572,39],[573,39],[574,39],[575,39],[576,39],[577,39],[578,39],[580,39],[579,39],[581,39],[582,39],[583,39],[584,39],[585,39],[586,39],[587,39],[588,39],[589,39],[593,39],[590,39],[591,39],[592,39],[594,39],[595,39],[596,39],[598,39],[597,39],[599,39],[600,39],[601,39],[602,39],[603,39],[604,39],[605,39],[606,39],[607,39],[608,39],[609,39],[610,39],[611,39],[612,39],[613,39],[614,39],[615,39],[616,39],[617,39],[618,39],[619,39],[620,39],[621,39],[622,39],[623,39],[624,39],[625,39],[626,39],[627,39],[628,39],[629,39],[630,39],[631,39],[632,39],[633,39],[634,39],[635,39],[636,39],[637,39],[638,39],[639,39],[640,39],[641,39],[642,39],[643,39],[644,39],[645,39],[646,39],[647,39],[648,39],[649,39],[650,39],[651,39],[653,39],[652,39],[654,39],[655,39],[656,39],[657,39],[658,39],[659,39],[660,39],[661,39],[662,39],[663,39],[664,39],[665,39],[666,39],[667,39],[687,39],[688,39],[689,39],[690,39],[691,39],[692,39],[693,39],[694,39],[695,39],[696,39],[697,39],[698,39],[699,39],[700,39],[701,39],[707,39],[702,39],[703,39],[704,39],[705,39],[706,39],[708,39],[709,39],[710,39],[711,39],[712,39],[713,39],[715,39],[716,39],[714,39],[717,39],[718,39],[719,39],[720,39],[721,39],[722,39],[723,39],[724,39],[725,39],[726,39],[727,39],[728,39],[729,39],[730,39],[731,39],[732,39],[733,39],[734,39],[735,39],[736,39],[737,39],[738,39],[739,39],[740,39],[741,39],[743,39],[742,39],[744,39],[745,39],[747,39],[746,39],[748,39],[749,39],[750,39],[751,39],[752,39],[754,39],[753,39],[755,39],[756,39],[757,39],[758,39],[759,39],[760,39],[761,39],[762,39],[763,39],[764,39],[765,39],[766,39],[767,39],[768,39],[773,39],[769,39],[770,39],[771,39],[772,39],[774,39],[775,39],[776,39],[777,39],[778,39],[779,39],[780,39],[781,39],[782,39],[783,39],[785,39],[784,39],[786,39],[787,39],[788,39],[789,39],[790,39],[791,39],[792,39],[793,39],[796,39],[794,39],[795,39],[797,39],[798,39],[799,39],[800,39],[801,39],[802,39],[803,39],[804,39],[806,39],[805,39],[917,135],[807,39],[808,39],[809,39],[810,39],[811,39],[812,39],[813,39],[814,39],[815,39],[816,39],[817,39],[819,39],[818,39],[820,39],[821,39],[822,39],[823,39],[824,39],[825,39],[826,39],[827,39],[829,39],[828,39],[830,39],[831,39],[832,39],[833,39],[834,39],[835,39],[836,39],[837,39],[838,39],[842,39],[839,39],[840,39],[841,39],[843,39],[844,39],[845,39],[847,39],[846,39],[848,39],[849,39],[850,39],[851,39],[852,39],[853,39],[854,39],[855,39],[856,39],[857,39],[858,39],[859,39],[860,39],[861,39],[862,39],[863,39],[864,39],[865,39],[866,39],[867,39],[868,39],[869,39],[870,39],[871,39],[872,39],[873,39],[874,39],[875,39],[876,39],[877,39],[878,39],[879,39],[880,39],[881,39],[882,39],[883,39],[884,39],[885,39],[886,39],[887,39],[888,39],[889,39],[890,39],[891,39],[892,39],[893,39],[894,39],[895,39],[896,39],[897,39],[898,39],[899,39],[900,39],[902,39],[901,39],[903,39],[904,39],[905,39],[906,39],[907,39],[908,39],[909,39],[910,39],[911,39],[912,39],[913,39],[914,39],[915,39],[916,39],[105,1],[431,136],[427,137],[414,1],[430,138],[423,139],[421,140],[420,140],[419,139],[416,140],[417,139],[425,141],[418,140],[415,139],[422,140],[428,142],[429,143],[424,144],[426,140],[202,145],[207,145],[205,146],[208,147],[201,145],[206,145],[203,145],[200,110],[230,148],[213,149],[212,150],[204,151],[229,152],[226,153],[228,154],[223,155],[225,156],[227,157],[222,158],[224,159],[199,1],[218,110],[217,110],[215,110],[219,160],[216,1],[221,161],[214,162],[211,110],[210,1],[220,1],[209,1],[198,163],[288,164],[289,165],[278,1],[282,1],[281,166],[280,167],[279,1],[277,168],[287,169],[283,1],[285,170],[286,171],[284,172],[240,1],[246,1],[250,173],[245,174],[244,87],[249,175],[248,1],[243,176],[242,111],[241,111],[239,1],[247,1],[238,168],[195,177],[194,178],[193,1],[152,179],[151,1],[155,180],[153,181],[154,181],[313,182],[314,183],[315,182],[316,184],[142,185],[369,1],[366,1],[365,1],[357,186],[371,187],[353,188],[367,189],[356,190],[355,191],[368,1],[360,192],[370,1],[362,193],[364,193],[363,193],[354,1],[361,1],[372,194],[433,195],[352,1],[1049,196],[1045,107],[1047,197],[1048,107],[146,110],[1050,198],[1052,199],[1055,200],[1056,201],[1058,202],[1059,1],[1060,203],[1061,204],[1067,205],[1068,1],[1069,1],[1071,206],[1072,207],[1070,208],[1073,209],[1074,210],[1075,211],[1076,212],[1077,213],[1078,214],[1079,215],[1080,216],[1081,217],[1082,218],[1083,202],[1085,1],[1084,1],[1087,219],[1088,220],[1086,219],[1051,1],[90,221],[48,222],[91,223],[49,224],[50,225],[51,226],[52,227],[53,228],[54,229],[55,230],[56,231],[57,232],[58,233],[59,234],[60,235],[47,1],[87,1],[61,236],[62,237],[63,238],[92,239],[64,240],[65,241],[66,242],[67,243],[68,244],[69,245],[70,246],[71,247],[72,248],[73,249],[74,250],[75,251],[76,252],[77,253],[78,254],[79,255],[89,256],[80,257],[81,258],[82,227],[83,259],[84,260],[88,261],[85,262],[86,263],[1089,1],[1090,1],[1091,1],[95,1],[1054,1],[1053,1],[93,1],[97,264],[1092,1],[96,1],[1093,1],[1057,1],[1094,265],[1095,1],[1096,266],[273,1],[157,1],[147,111],[141,1],[94,1],[150,1],[172,1],[169,267],[171,267],[170,267],[168,267],[178,268],[173,269],[177,1],[174,1],[176,1],[175,1],[164,267],[165,267],[166,267],[162,1],[163,1],[167,267],[1062,1],[1064,270],[1066,271],[1065,270],[1063,1],[158,1],[1015,39],[1039,1],[116,272],[106,273],[129,274],[126,274],[934,275],[108,276],[107,277],[110,278],[109,279],[100,1],[101,280],[115,281],[103,282],[104,283],[127,1],[99,1],[102,1],[123,284],[133,285],[134,286],[124,287],[125,288],[128,283],[1013,39],[131,1],[111,1],[113,289],[118,290],[122,291],[121,1],[132,292],[112,293],[130,293],[119,290],[117,1],[120,1],[114,294],[1014,295],[137,296],[935,297],[330,298],[136,110],[135,299],[359,300],[358,1],[960,301],[962,302],[963,303],[959,39],[961,39],[967,304],[958,305],[957,39],[968,306],[954,39],[956,307],[955,305],[966,308],[965,309],[964,310],[98,311],[378,1],[385,312],[380,313],[381,314],[382,314],[383,315],[384,315],[379,316],[432,317],[931,318],[10,1],[11,1],[13,1],[12,1],[2,1],[14,1],[15,1],[16,1],[17,1],[18,1],[19,1],[20,1],[21,1],[3,1],[4,1],[25,1],[22,1],[23,1],[24,1],[26,1],[27,1],[28,1],[5,1],[29,1],[30,1],[31,1],[32,1],[6,1],[33,1],[34,1],[35,1],[36,1],[7,1],[41,1],[37,1],[38,1],[39,1],[40,1],[8,1],[45,1],[42,1],[43,1],[44,1],[1,1],[9,1],[46,1],[405,319],[400,320],[390,321],[403,322],[409,323],[396,324],[398,325],[399,320],[402,322],[404,319],[389,326],[395,327],[408,328],[401,322],[397,329],[392,330],[407,325],[406,1],[394,331],[393,1],[391,1],[161,332],[160,1],[1016,333],[350,334],[1019,335],[1020,336],[1023,337],[1021,338],[1024,339],[1028,340],[1026,341],[1031,342],[1027,343],[1025,344],[1022,345],[1030,346],[1018,347],[978,348],[1029,349],[1032,350],[1017,351],[1033,352],[1034,353],[1035,354],[1041,1],[322,1],[323,1],[319,355],[351,356],[976,357],[179,358],[985,359],[329,360],[1042,1],[1043,1],[1036,361],[373,362],[374,1],[328,1],[375,1],[233,1],[326,1],[376,1],[377,363],[231,364],[387,365],[388,366],[386,367],[1037,1],[411,368],[1009,369],[148,370],[325,371],[991,1],[942,372],[412,25],[318,1],[995,373],[196,1],[413,374],[180,375],[181,376],[274,377],[275,378],[312,379],[410,380],[317,381]],"exportedModulesMap":[[139,1],[140,2],[143,1],[185,3],[187,4],[308,5],[192,6],[291,7],[292,8],[294,9],[296,10],[298,11],[300,12],[302,13],[303,14],[435,15],[437,16],[671,17],[997,18],[998,19],[999,20],[927,21],[928,22],[1000,23],[311,1],[929,24],[930,1],[950,1],[972,25],[973,26],[1001,27],[971,28],[977,26],[1002,29],[1003,30],[1004,31],[1005,32],[197,1],[235,33],[268,34],[269,1],[270,35],[271,36],[272,37],[932,38],[933,38],[436,39],[980,41],[984,43],[309,45],[982,382],[669,47],[936,48],[937,49],[938,1],[939,50],[940,39],[947,51],[949,52],[941,53],[946,54],[951,55],[952,56],[953,57],[969,58],[948,39],[970,59],[434,60],[986,61],[987,62],[988,63],[994,64],[989,65],[993,66],[990,67],[996,68],[992,69],[310,1],[974,70],[945,71],[975,72],[944,1],[333,73],[335,74],[1006,75],[1007,39],[1008,39],[334,76],[1010,77],[1011,39],[336,39],[670,78],[943,79],[338,80],[332,81],[337,82],[339,83],[1012,84],[1038,1],[159,85],[234,86],[304,87],[320,88],[305,89],[306,90],[307,91],[145,1],[156,92],[144,1],[232,93],[340,94],[341,95],[321,1],[342,1],[182,96],[327,97],[149,1],[183,98],[324,1],[331,99],[189,100],[184,101],[186,100],[343,94],[344,94],[345,94],[188,100],[299,102],[290,103],[276,104],[346,94],[293,94],[295,101],[297,101],[190,100],[301,101],[347,101],[348,101],[191,101],[349,94],[138,105],[1040,106],[1046,107],[1044,1],[256,108],[262,109],[263,110],[252,111],[236,1],[267,112],[264,113],[259,114],[237,1],[260,115],[255,116],[258,117],[261,118],[257,119],[254,120],[265,1],[253,1],[266,121],[251,122],[919,123],[922,39],[924,124],[921,125],[923,126],[920,127],[925,125],[926,128],[918,1],[674,129],[675,130],[676,129],[677,129],[682,129],[678,129],[679,129],[680,129],[681,129],[683,131],[684,131],[685,132],[686,133],[672,39],[673,129],[438,39],[439,39],[440,39],[441,39],[442,39],[443,39],[444,39],[445,39],[446,39],[447,39],[448,39],[449,39],[450,39],[451,39],[452,39],[458,39],[453,39],[454,39],[455,39],[456,39],[457,39],[459,39],[460,39],[461,39],[462,39],[463,39],[464,39],[466,39],[467,39],[465,39],[468,39],[469,39],[470,39],[471,39],[472,39],[473,39],[474,39],[475,39],[476,39],[477,39],[478,39],[479,39],[480,39],[481,39],[482,39],[483,39],[484,39],[485,39],[486,39],[487,39],[488,39],[489,39],[490,39],[491,39],[492,39],[494,39],[493,39],[495,39],[496,39],[498,39],[497,39],[499,39],[500,39],[501,39],[502,39],[503,39],[505,39],[504,39],[506,39],[507,39],[508,39],[509,39],[510,39],[511,39],[512,39],[513,39],[514,39],[515,39],[516,39],[517,39],[518,39],[519,39],[524,39],[520,39],[521,39],[522,39],[523,39],[525,39],[526,39],[527,39],[528,39],[529,39],[530,39],[531,39],[532,39],[533,39],[534,39],[536,39],[535,39],[537,39],[538,39],[539,39],[540,39],[541,39],[542,39],[543,39],[544,39],[547,39],[545,39],[546,39],[548,39],[549,39],[550,39],[551,39],[552,39],[553,39],[554,39],[555,39],[557,39],[556,39],[668,134],[558,39],[559,39],[560,39],[561,39],[562,39],[563,39],[564,39],[565,39],[566,39],[567,39],[568,39],[570,39],[569,39],[571,39],[572,39],[573,39],[574,39],[575,39],[576,39],[577,39],[578,39],[580,39],[579,39],[581,39],[582,39],[583,39],[584,39],[585,39],[586,39],[587,39],[588,39],[589,39],[593,39],[590,39],[591,39],[592,39],[594,39],[595,39],[596,39],[598,39],[597,39],[599,39],[600,39],[601,39],[602,39],[603,39],[604,39],[605,39],[606,39],[607,39],[608,39],[609,39],[610,39],[611,39],[612,39],[613,39],[614,39],[615,39],[616,39],[617,39],[618,39],[619,39],[620,39],[621,39],[622,39],[623,39],[624,39],[625,39],[626,39],[627,39],[628,39],[629,39],[630,39],[631,39],[632,39],[633,39],[634,39],[635,39],[636,39],[637,39],[638,39],[639,39],[640,39],[641,39],[642,39],[643,39],[644,39],[645,39],[646,39],[647,39],[648,39],[649,39],[650,39],[651,39],[653,39],[652,39],[654,39],[655,39],[656,39],[657,39],[658,39],[659,39],[660,39],[661,39],[662,39],[663,39],[664,39],[665,39],[666,39],[667,39],[687,39],[688,39],[689,39],[690,39],[691,39],[692,39],[693,39],[694,39],[695,39],[696,39],[697,39],[698,39],[699,39],[700,39],[701,39],[707,39],[702,39],[703,39],[704,39],[705,39],[706,39],[708,39],[709,39],[710,39],[711,39],[712,39],[713,39],[715,39],[716,39],[714,39],[717,39],[718,39],[719,39],[720,39],[721,39],[722,39],[723,39],[724,39],[725,39],[726,39],[727,39],[728,39],[729,39],[730,39],[731,39],[732,39],[733,39],[734,39],[735,39],[736,39],[737,39],[738,39],[739,39],[740,39],[741,39],[743,39],[742,39],[744,39],[745,39],[747,39],[746,39],[748,39],[749,39],[750,39],[751,39],[752,39],[754,39],[753,39],[755,39],[756,39],[757,39],[758,39],[759,39],[760,39],[761,39],[762,39],[763,39],[764,39],[765,39],[766,39],[767,39],[768,39],[773,39],[769,39],[770,39],[771,39],[772,39],[774,39],[775,39],[776,39],[777,39],[778,39],[779,39],[780,39],[781,39],[782,39],[783,39],[785,39],[784,39],[786,39],[787,39],[788,39],[789,39],[790,39],[791,39],[792,39],[793,39],[796,39],[794,39],[795,39],[797,39],[798,39],[799,39],[800,39],[801,39],[802,39],[803,39],[804,39],[806,39],[805,39],[917,135],[807,39],[808,39],[809,39],[810,39],[811,39],[812,39],[813,39],[814,39],[815,39],[816,39],[817,39],[819,39],[818,39],[820,39],[821,39],[822,39],[823,39],[824,39],[825,39],[826,39],[827,39],[829,39],[828,39],[830,39],[831,39],[832,39],[833,39],[834,39],[835,39],[836,39],[837,39],[838,39],[842,39],[839,39],[840,39],[841,39],[843,39],[844,39],[845,39],[847,39],[846,39],[848,39],[849,39],[850,39],[851,39],[852,39],[853,39],[854,39],[855,39],[856,39],[857,39],[858,39],[859,39],[860,39],[861,39],[862,39],[863,39],[864,39],[865,39],[866,39],[867,39],[868,39],[869,39],[870,39],[871,39],[872,39],[873,39],[874,39],[875,39],[876,39],[877,39],[878,39],[879,39],[880,39],[881,39],[882,39],[883,39],[884,39],[885,39],[886,39],[887,39],[888,39],[889,39],[890,39],[891,39],[892,39],[893,39],[894,39],[895,39],[896,39],[897,39],[898,39],[899,39],[900,39],[902,39],[901,39],[903,39],[904,39],[905,39],[906,39],[907,39],[908,39],[909,39],[910,39],[911,39],[912,39],[913,39],[914,39],[915,39],[916,39],[105,1],[431,136],[427,137],[414,1],[430,138],[423,139],[421,140],[420,140],[419,139],[416,140],[417,139],[425,141],[418,140],[415,139],[422,140],[428,142],[429,143],[424,144],[426,140],[202,145],[207,145],[205,146],[208,147],[201,145],[206,145],[203,145],[200,110],[230,148],[213,149],[212,150],[204,151],[229,152],[226,153],[228,154],[223,155],[225,156],[227,157],[222,158],[224,159],[199,1],[218,110],[217,110],[215,110],[219,160],[216,1],[221,161],[214,162],[211,110],[210,1],[220,1],[209,1],[198,163],[288,164],[289,165],[278,1],[282,1],[281,166],[280,167],[279,1],[277,168],[287,169],[283,1],[285,170],[286,171],[284,172],[240,1],[246,1],[250,173],[245,174],[244,87],[249,175],[248,1],[243,176],[242,111],[241,111],[239,1],[247,1],[238,168],[195,177],[194,178],[193,1],[152,179],[151,1],[155,180],[153,181],[154,181],[313,182],[314,183],[315,182],[316,184],[142,185],[369,1],[366,1],[365,1],[357,186],[371,187],[353,188],[367,189],[356,190],[355,191],[368,1],[360,192],[370,1],[362,193],[364,193],[363,193],[354,1],[361,1],[372,194],[433,195],[352,1],[1049,196],[1045,107],[1047,197],[1048,107],[146,110],[1050,198],[1052,199],[1055,200],[1056,201],[1058,202],[1059,1],[1060,203],[1061,204],[1067,205],[1068,1],[1069,1],[1071,206],[1072,207],[1070,208],[1073,209],[1074,210],[1075,211],[1076,212],[1077,213],[1078,214],[1079,215],[1080,216],[1081,217],[1082,218],[1083,202],[1085,1],[1084,1],[1087,219],[1088,220],[1086,219],[1051,1],[90,221],[48,222],[91,223],[49,224],[50,225],[51,226],[52,227],[53,228],[54,229],[55,230],[56,231],[57,232],[58,233],[59,234],[60,235],[47,1],[87,1],[61,236],[62,237],[63,238],[92,239],[64,240],[65,241],[66,242],[67,243],[68,244],[69,245],[70,246],[71,247],[72,248],[73,249],[74,250],[75,251],[76,252],[77,253],[78,254],[79,255],[89,256],[80,257],[81,258],[82,227],[83,259],[84,260],[88,261],[85,262],[86,263],[1089,1],[1090,1],[1091,1],[95,1],[1054,1],[1053,1],[93,1],[97,264],[1092,1],[96,1],[1093,1],[1057,1],[1094,265],[1095,1],[1096,266],[273,1],[157,1],[147,111],[141,1],[94,1],[150,1],[172,1],[169,267],[171,267],[170,267],[168,267],[178,268],[173,269],[177,1],[174,1],[176,1],[175,1],[164,267],[165,267],[166,267],[162,1],[163,1],[167,267],[1062,1],[1064,270],[1066,271],[1065,270],[1063,1],[158,1],[1015,39],[1039,1],[116,272],[106,273],[129,274],[126,274],[934,275],[108,276],[107,277],[110,278],[109,279],[100,1],[101,280],[115,281],[103,282],[104,283],[127,1],[99,1],[102,1],[123,284],[133,285],[134,286],[124,287],[125,288],[128,283],[1013,39],[131,1],[111,1],[113,289],[118,290],[122,291],[121,1],[132,292],[112,293],[130,293],[119,290],[117,1],[120,1],[114,294],[1014,295],[137,296],[935,297],[330,298],[136,110],[135,299],[359,300],[358,1],[960,301],[962,302],[963,303],[959,39],[961,39],[967,304],[958,305],[957,39],[968,306],[954,39],[956,307],[955,305],[966,308],[965,309],[964,310],[98,311],[378,1],[385,312],[380,313],[381,314],[382,314],[383,315],[384,315],[379,316],[432,317],[931,318],[10,1],[11,1],[13,1],[12,1],[2,1],[14,1],[15,1],[16,1],[17,1],[18,1],[19,1],[20,1],[21,1],[3,1],[4,1],[25,1],[22,1],[23,1],[24,1],[26,1],[27,1],[28,1],[5,1],[29,1],[30,1],[31,1],[32,1],[6,1],[33,1],[34,1],[35,1],[36,1],[7,1],[41,1],[37,1],[38,1],[39,1],[40,1],[8,1],[45,1],[42,1],[43,1],[44,1],[1,1],[9,1],[46,1],[405,319],[400,320],[390,321],[403,322],[409,323],[396,324],[398,325],[399,320],[402,322],[404,319],[389,326],[395,327],[408,328],[401,322],[397,329],[392,330],[407,325],[406,1],[394,331],[393,1],[391,1],[161,332],[160,1],[1016,333],[350,334],[1019,335],[1020,336],[1023,337],[1021,338],[1024,339],[1028,340],[1026,341],[1031,342],[1027,343],[1025,344],[1022,345],[1030,346],[1018,347],[978,348],[1029,349],[1032,350],[1017,351],[1033,352],[1034,353],[1035,354],[1041,1],[322,1],[323,1],[319,355],[351,356],[976,357],[179,358],[985,359],[329,360],[1042,1],[1043,1],[1036,361],[373,362],[374,1],[328,1],[375,1],[233,1],[326,1],[376,1],[377,363],[231,364],[387,365],[388,366],[386,367],[1037,1],[411,368],[1009,369],[148,370],[325,371],[991,1],[942,372],[412,25],[318,1],[995,373],[196,1],[413,383],[180,375],[181,376],[274,377],[275,378],[312,379],[410,380],[317,381]],"semanticDiagnosticsPerFile":[139,140,143,185,187,308,192,291,292,294,296,298,300,302,303,435,437,671,997,998,999,927,928,1000,311,929,930,950,972,973,1001,971,977,1002,1003,1004,1005,197,235,268,269,270,271,272,932,933,436,979,980,981,984,983,309,982,669,936,937,938,939,940,947,949,941,946,951,952,953,969,948,970,434,986,987,988,994,989,993,990,996,992,310,974,945,975,944,333,335,1006,1007,1008,334,1010,1011,336,670,943,338,332,337,339,1012,1038,159,234,304,320,305,306,307,145,156,144,232,340,341,321,342,182,327,149,183,324,331,189,184,186,343,344,345,188,299,290,276,346,293,295,297,190,301,347,348,191,349,138,1040,1046,1044,256,262,263,252,236,267,264,259,237,260,255,258,261,257,254,265,253,266,251,919,922,924,921,923,920,925,926,918,674,675,676,677,682,678,679,680,681,683,684,685,686,672,673,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,458,453,454,455,456,457,459,460,461,462,463,464,466,467,465,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,494,493,495,496,498,497,499,500,501,502,503,505,504,506,507,508,509,510,511,512,513,514,515,516,517,518,519,524,520,521,522,523,525,526,527,528,529,530,531,532,533,534,536,535,537,538,539,540,541,542,543,544,547,545,546,548,549,550,551,552,553,554,555,557,556,668,558,559,560,561,562,563,564,565,566,567,568,570,569,571,572,573,574,575,576,577,578,580,579,581,582,583,584,585,586,587,588,589,593,590,591,592,594,595,596,598,597,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,653,652,654,655,656,657,658,659,660,661,662,663,664,665,666,667,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,707,702,703,704,705,706,708,709,710,711,712,713,715,716,714,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,743,742,744,745,747,746,748,749,750,751,752,754,753,755,756,757,758,759,760,761,762,763,764,765,766,767,768,773,769,770,771,772,774,775,776,777,778,779,780,781,782,783,785,784,786,787,788,789,790,791,792,793,796,794,795,797,798,799,800,801,802,803,804,806,805,917,807,808,809,810,811,812,813,814,815,816,817,819,818,820,821,822,823,824,825,826,827,829,828,830,831,832,833,834,835,836,837,838,842,839,840,841,843,844,845,847,846,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,902,901,903,904,905,906,907,908,909,910,911,912,913,914,915,916,105,431,427,414,430,423,421,420,419,416,417,425,418,415,422,428,429,424,426,202,207,205,208,201,206,203,200,230,213,212,204,229,226,228,223,225,227,222,224,199,218,217,215,219,216,221,214,211,210,220,209,198,288,289,278,282,281,280,279,277,287,283,285,286,284,240,246,250,245,244,249,248,243,242,241,239,247,238,195,194,193,152,151,155,153,154,313,314,315,316,142,369,366,365,357,371,353,367,356,355,368,360,370,362,364,363,354,361,372,433,352,1049,1045,1047,1048,146,1050,1052,1055,1056,1058,1059,1060,1061,1067,1068,1069,1071,1072,1070,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1085,1084,1087,1088,1086,1051,90,48,91,49,50,51,52,53,54,55,56,57,58,59,60,47,87,61,62,63,92,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,89,80,81,82,83,84,88,85,86,1089,1090,1091,95,1054,1053,93,97,1092,96,1093,1057,1094,1095,1096,273,157,147,141,94,150,172,169,171,170,168,178,173,177,174,176,175,164,165,166,162,163,167,1062,1064,1066,1065,1063,158,1015,1039,116,106,129,126,934,108,107,110,109,100,101,115,103,104,127,99,102,123,133,134,124,125,128,1013,131,111,113,118,122,121,132,112,130,119,117,120,114,1014,137,935,330,136,135,359,358,960,962,963,959,961,967,958,957,968,954,956,955,966,965,964,98,378,385,380,381,382,383,384,379,432,931,10,11,13,12,2,14,15,16,17,18,19,20,21,3,4,25,22,23,24,26,27,28,5,29,30,31,32,6,33,34,35,36,7,41,37,38,39,40,8,45,42,43,44,1,9,46,405,400,390,403,409,396,398,399,402,404,389,395,408,401,397,392,407,406,394,393,391,161,160,1016,350,1019,1020,1023,1021,1024,1028,1026,1031,1027,1025,1022,1030,1018,978,1029,1032,1017,1033,1034,1035,1041,322,323,319,351,976,179,985,329,1042,1043,1036,373,374,328,375,233,326,376,377,231,387,388,386,1037,411,1009,148,325,991,942,412,318,995,196,413,180,181,274,275,312,410,317],"affectedFilesPendingEmit":[[139,1],[140,1],[143,1],[185,1],[187,1],[308,1],[192,1],[291,1],[292,1],[294,1],[296,1],[298,1],[300,1],[302,1],[303,1],[435,1],[437,1],[671,1],[997,1],[998,1],[999,1],[927,1],[928,1],[1000,1],[311,1],[929,1],[930,1],[950,1],[972,1],[973,1],[1001,1],[971,1],[977,1],[1002,1],[1003,1],[1004,1],[1005,1],[197,1],[235,1],[268,1],[269,1],[270,1],[271,1],[272,1],[932,1],[933,1],[436,1],[979,1],[980,1],[981,1],[984,1],[983,1],[309,1],[982,1],[669,1],[936,1],[937,1],[938,1],[939,1],[940,1],[947,1],[949,1],[941,1],[946,1],[951,1],[952,1],[953,1],[969,1],[948,1],[970,1],[434,1],[986,1],[987,1],[988,1],[994,1],[989,1],[993,1],[990,1],[996,1],[992,1],[310,1],[974,1],[945,1],[975,1],[944,1],[333,1],[335,1],[1006,1],[1007,1],[1008,1],[334,1],[1010,1],[1011,1],[336,1],[670,1],[943,1],[338,1],[332,1],[337,1],[339,1],[1012,1],[1038,1],[159,1],[234,1],[304,1],[320,1],[305,1],[306,1],[307,1],[145,1],[156,1],[144,1],[232,1],[340,1],[341,1],[321,1],[342,1],[182,1],[327,1],[149,1],[183,1],[324,1],[331,1],[189,1],[184,1],[186,1],[343,1],[344,1],[345,1],[188,1],[299,1],[290,1],[276,1],[346,1],[293,1],[295,1],[297,1],[190,1],[301,1],[347,1],[348,1],[191,1],[349,1],[138,1],[1040,1],[1046,1],[1044,1],[256,1],[262,1],[263,1],[252,1],[236,1],[267,1],[264,1],[259,1],[237,1],[260,1],[255,1],[258,1],[261,1],[257,1],[254,1],[265,1],[253,1],[266,1],[251,1],[919,1],[922,1],[924,1],[921,1],[923,1],[920,1],[925,1],[926,1],[918,1],[674,1],[675,1],[676,1],[677,1],[682,1],[678,1],[679,1],[680,1],[681,1],[683,1],[684,1],[685,1],[686,1],[672,1],[673,1],[438,1],[439,1],[440,1],[441,1],[442,1],[443,1],[444,1],[445,1],[446,1],[447,1],[448,1],[449,1],[450,1],[451,1],[452,1],[458,1],[453,1],[454,1],[455,1],[456,1],[457,1],[459,1],[460,1],[461,1],[462,1],[463,1],[464,1],[466,1],[467,1],[465,1],[468,1],[469,1],[470,1],[471,1],[472,1],[473,1],[474,1],[475,1],[476,1],[477,1],[478,1],[479,1],[480,1],[481,1],[482,1],[483,1],[484,1],[485,1],[486,1],[487,1],[488,1],[489,1],[490,1],[491,1],[492,1],[494,1],[493,1],[495,1],[496,1],[498,1],[497,1],[499,1],[500,1],[501,1],[502,1],[503,1],[505,1],[504,1],[506,1],[507,1],[508,1],[509,1],[510,1],[511,1],[512,1],[513,1],[514,1],[515,1],[516,1],[517,1],[518,1],[519,1],[524,1],[520,1],[521,1],[522,1],[523,1],[525,1],[526,1],[527,1],[528,1],[529,1],[530,1],[531,1],[532,1],[533,1],[534,1],[536,1],[535,1],[537,1],[538,1],[539,1],[540,1],[541,1],[542,1],[543,1],[544,1],[547,1],[545,1],[546,1],[548,1],[549,1],[550,1],[551,1],[552,1],[553,1],[554,1],[555,1],[557,1],[556,1],[668,1],[558,1],[559,1],[560,1],[561,1],[562,1],[563,1],[564,1],[565,1],[566,1],[567,1],[568,1],[570,1],[569,1],[571,1],[572,1],[573,1],[574,1],[575,1],[576,1],[577,1],[578,1],[580,1],[579,1],[581,1],[582,1],[583,1],[584,1],[585,1],[586,1],[587,1],[588,1],[589,1],[593,1],[590,1],[591,1],[592,1],[594,1],[595,1],[596,1],[598,1],[597,1],[599,1],[600,1],[601,1],[602,1],[603,1],[604,1],[605,1],[606,1],[607,1],[608,1],[609,1],[610,1],[611,1],[612,1],[613,1],[614,1],[615,1],[616,1],[617,1],[618,1],[619,1],[620,1],[621,1],[622,1],[623,1],[624,1],[625,1],[626,1],[627,1],[628,1],[629,1],[630,1],[631,1],[632,1],[633,1],[634,1],[635,1],[636,1],[637,1],[638,1],[639,1],[640,1],[641,1],[642,1],[643,1],[644,1],[645,1],[646,1],[647,1],[648,1],[649,1],[650,1],[651,1],[653,1],[652,1],[654,1],[655,1],[656,1],[657,1],[658,1],[659,1],[660,1],[661,1],[662,1],[663,1],[664,1],[665,1],[666,1],[667,1],[687,1],[688,1],[689,1],[690,1],[691,1],[692,1],[693,1],[694,1],[695,1],[696,1],[697,1],[698,1],[699,1],[700,1],[701,1],[707,1],[702,1],[703,1],[704,1],[705,1],[706,1],[708,1],[709,1],[710,1],[711,1],[712,1],[713,1],[715,1],[716,1],[714,1],[717,1],[718,1],[719,1],[720,1],[721,1],[722,1],[723,1],[724,1],[725,1],[726,1],[727,1],[728,1],[729,1],[730,1],[731,1],[732,1],[733,1],[734,1],[735,1],[736,1],[737,1],[738,1],[739,1],[740,1],[741,1],[743,1],[742,1],[744,1],[745,1],[747,1],[746,1],[748,1],[749,1],[750,1],[751,1],[752,1],[754,1],[753,1],[755,1],[756,1],[757,1],[758,1],[759,1],[760,1],[761,1],[762,1],[763,1],[764,1],[765,1],[766,1],[767,1],[768,1],[773,1],[769,1],[770,1],[771,1],[772,1],[774,1],[775,1],[776,1],[777,1],[778,1],[779,1],[780,1],[781,1],[782,1],[783,1],[785,1],[784,1],[786,1],[787,1],[788,1],[789,1],[790,1],[791,1],[792,1],[793,1],[796,1],[794,1],[795,1],[797,1],[798,1],[799,1],[800,1],[801,1],[802,1],[803,1],[804,1],[806,1],[805,1],[917,1],[807,1],[808,1],[809,1],[810,1],[811,1],[812,1],[813,1],[814,1],[815,1],[816,1],[817,1],[819,1],[818,1],[820,1],[821,1],[822,1],[823,1],[824,1],[825,1],[826,1],[827,1],[829,1],[828,1],[830,1],[831,1],[832,1],[833,1],[834,1],[835,1],[836,1],[837,1],[838,1],[842,1],[839,1],[840,1],[841,1],[843,1],[844,1],[845,1],[847,1],[846,1],[848,1],[849,1],[850,1],[851,1],[852,1],[853,1],[854,1],[855,1],[856,1],[857,1],[858,1],[859,1],[860,1],[861,1],[862,1],[863,1],[864,1],[865,1],[866,1],[867,1],[868,1],[869,1],[870,1],[871,1],[872,1],[873,1],[874,1],[875,1],[876,1],[877,1],[878,1],[879,1],[880,1],[881,1],[882,1],[883,1],[884,1],[885,1],[886,1],[887,1],[888,1],[889,1],[890,1],[891,1],[892,1],[893,1],[894,1],[895,1],[896,1],[897,1],[898,1],[899,1],[900,1],[902,1],[901,1],[903,1],[904,1],[905,1],[906,1],[907,1],[908,1],[909,1],[910,1],[911,1],[912,1],[913,1],[914,1],[915,1],[916,1],[105,1],[431,1],[427,1],[414,1],[430,1],[423,1],[421,1],[420,1],[419,1],[416,1],[417,1],[425,1],[418,1],[415,1],[422,1],[428,1],[429,1],[424,1],[426,1],[202,1],[207,1],[205,1],[208,1],[201,1],[206,1],[203,1],[200,1],[230,1],[213,1],[212,1],[204,1],[229,1],[226,1],[228,1],[223,1],[225,1],[227,1],[222,1],[224,1],[199,1],[218,1],[217,1],[215,1],[219,1],[216,1],[221,1],[214,1],[211,1],[210,1],[220,1],[209,1],[198,1],[288,1],[289,1],[278,1],[282,1],[281,1],[280,1],[279,1],[277,1],[287,1],[283,1],[285,1],[286,1],[284,1],[240,1],[246,1],[250,1],[245,1],[244,1],[249,1],[248,1],[243,1],[242,1],[241,1],[239,1],[247,1],[238,1],[195,1],[194,1],[193,1],[152,1],[151,1],[155,1],[153,1],[154,1],[313,1],[314,1],[315,1],[316,1],[142,1],[369,1],[366,1],[365,1],[357,1],[371,1],[353,1],[367,1],[356,1],[355,1],[368,1],[360,1],[370,1],[362,1],[364,1],[363,1],[354,1],[361,1],[372,1],[433,1],[352,1],[1049,1],[1045,1],[1047,1],[1048,1],[146,1],[1050,1],[1052,1],[1055,1],[1056,1],[1058,1],[1059,1],[1060,1],[1061,1],[1067,1],[1068,1],[1069,1],[1071,1],[1072,1],[1070,1],[1073,1],[1074,1],[1075,1],[1076,1],[1077,1],[1078,1],[1079,1],[1080,1],[1081,1],[1082,1],[1083,1],[1085,1],[1084,1],[1087,1],[1088,1],[1086,1],[1051,1],[90,1],[48,1],[91,1],[49,1],[50,1],[51,1],[52,1],[53,1],[54,1],[55,1],[56,1],[57,1],[58,1],[59,1],[60,1],[47,1],[87,1],[61,1],[62,1],[63,1],[92,1],[64,1],[65,1],[66,1],[67,1],[68,1],[69,1],[70,1],[71,1],[72,1],[73,1],[74,1],[75,1],[76,1],[77,1],[78,1],[79,1],[89,1],[80,1],[81,1],[82,1],[83,1],[84,1],[88,1],[85,1],[86,1],[1089,1],[1090,1],[1091,1],[95,1],[1054,1],[1053,1],[93,1],[97,1],[1092,1],[96,1],[1093,1],[1057,1],[1094,1],[1095,1],[1096,1],[273,1],[157,1],[147,1],[141,1],[94,1],[150,1],[172,1],[169,1],[171,1],[170,1],[168,1],[178,1],[173,1],[177,1],[174,1],[176,1],[175,1],[164,1],[165,1],[166,1],[162,1],[163,1],[167,1],[1062,1],[1064,1],[1066,1],[1065,1],[1063,1],[158,1],[1015,1],[1039,1],[116,1],[106,1],[129,1],[126,1],[934,1],[108,1],[107,1],[110,1],[109,1],[100,1],[101,1],[115,1],[103,1],[104,1],[127,1],[99,1],[102,1],[123,1],[133,1],[134,1],[124,1],[125,1],[128,1],[1013,1],[131,1],[111,1],[113,1],[118,1],[122,1],[121,1],[132,1],[112,1],[130,1],[119,1],[117,1],[120,1],[114,1],[1014,1],[137,1],[935,1],[330,1],[136,1],[135,1],[359,1],[358,1],[960,1],[962,1],[963,1],[959,1],[961,1],[967,1],[958,1],[957,1],[968,1],[954,1],[956,1],[955,1],[966,1],[965,1],[964,1],[98,1],[378,1],[385,1],[380,1],[381,1],[382,1],[383,1],[384,1],[379,1],[432,1],[931,1],[2,1],[3,1],[4,1],[5,1],[6,1],[7,1],[8,1],[9,1],[405,1],[400,1],[390,1],[403,1],[409,1],[396,1],[398,1],[399,1],[402,1],[404,1],[389,1],[395,1],[408,1],[401,1],[397,1],[392,1],[407,1],[406,1],[394,1],[393,1],[391,1],[161,1],[160,1],[1016,1],[350,1],[1019,1],[1020,1],[1023,1],[1021,1],[1024,1],[1028,1],[1026,1],[1031,1],[1027,1],[1025,1],[1022,1],[1030,1],[1018,1],[978,1],[1029,1],[1032,1],[1017,1],[1033,1],[1034,1],[1035,1],[1041,1],[322,1],[323,1],[319,1],[351,1],[976,1],[179,1],[985,1],[329,1],[1042,1],[1043,1],[1036,1],[373,1],[374,1],[328,1],[375,1],[233,1],[326,1],[376,1],[377,1],[231,1],[387,1],[388,1],[386,1],[1037,1],[411,1],[1009,1],[148,1],[325,1],[991,1],[942,1],[412,1],[318,1],[995,1],[196,1],[413,1],[180,1],[181,1],[274,1],[275,1],[312,1],[410,1],[317,1]]},"version":"4.5.4"} \ No newline at end of file +{"program":{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/globals.global.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/ts3.6/base.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/base.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/prop-types/index.d.ts","./node_modules/@types/scheduler/tracing.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/styled-jsx/index.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/server/image-config.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/@next/env/types/index.d.ts","./node_modules/next/dist/build/webpack/plugins/build-manifest-plugin.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/server/api-utils.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/get-middleware-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/next/dist/shared/lib/router/utils/sorted-routes.d.ts","./node_modules/next/dist/shared/lib/router/utils/is-dynamic.d.ts","./node_modules/next/dist/shared/lib/router/utils/index.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/server/router.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/server/font-utils.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/shared/lib/i18n/normalize-locale-path.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-next-url.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/types/index.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/image-types/global.d.ts","./next-env.d.ts","./@types/buffer-layout.d.ts","./@types/index.d.ts","./node_modules/buffer/index.d.ts","./node_modules/@solana/web3.js/lib/index.d.ts","./@types/types.ts","./models/core/errors.ts","./models/core/accounts.ts","./node_modules/@types/bn.js/index.d.ts","./node_modules/borsh/lib/index.d.ts","./utils/borsh.ts","./models/registry/constants.ts","./node_modules/eventemitter3/index.d.ts","./node_modules/@solana/wallet-adapter-base/lib/errors.d.ts","./node_modules/@solana/wallet-adapter-base/lib/adapter.d.ts","./node_modules/@solana/wallet-adapter-base/lib/poll.d.ts","./node_modules/@solana/wallet-adapter-base/lib/signer.d.ts","./node_modules/@solana/wallet-adapter-base/lib/index.d.ts","./models/core/api.ts","./node_modules/bignumber.js/bignumber.d.ts","./node_modules/moment/ts3.1-typings/moment.d.ts","./models/accounts.ts","./node_modules/zustand/vanilla.d.ts","./node_modules/zustand/index.d.ts","./node_modules/immer/dist/utils/env.d.ts","./node_modules/immer/dist/utils/errors.d.ts","./node_modules/immer/dist/types/types-external.d.ts","./node_modules/immer/dist/types/types-internal.d.ts","./node_modules/immer/dist/utils/common.d.ts","./node_modules/immer/dist/utils/plugins.d.ts","./node_modules/immer/dist/core/scope.d.ts","./node_modules/immer/dist/core/finalize.d.ts","./node_modules/immer/dist/core/proxy.d.ts","./node_modules/immer/dist/core/immerclass.d.ts","./node_modules/immer/dist/core/current.d.ts","./node_modules/immer/dist/internal.d.ts","./node_modules/immer/dist/plugins/es5.d.ts","./node_modules/immer/dist/plugins/patches.d.ts","./node_modules/immer/dist/plugins/mapset.d.ts","./node_modules/immer/dist/plugins/all.d.ts","./node_modules/immer/dist/immer.d.ts","./stores/usenotificationstore.tsx","./utils/notifications.tsx","./node_modules/@project-serum/common/node_modules/@solana/web3.js/lib/index.d.ts","./node_modules/@project-serum/common/dist/lib/provider.d.ts","./node_modules/@solana/spl-token/node_modules/buffer/index.d.ts","./node_modules/@solana/spl-token/node_modules/@solana/web3.js/lib/index.d.ts","./node_modules/@solana/spl-token/lib/index.d.ts","./node_modules/@project-serum/common/dist/lib/token.d.ts","./node_modules/@project-serum/common/dist/lib/simulate-transaction.d.ts","./node_modules/@project-serum/common/node_modules/superstruct/lib/struct-error.d.ts","./node_modules/@project-serum/common/node_modules/superstruct/lib/types.d.ts","./node_modules/@project-serum/common/node_modules/superstruct/lib/struct.d.ts","./node_modules/@project-serum/common/node_modules/superstruct/lib/superstruct.d.ts","./node_modules/@project-serum/common/node_modules/superstruct/lib/index.d.ts","./node_modules/@project-serum/common/dist/lib/connection.d.ts","./node_modules/@project-serum/common/dist/lib/index.d.ts","./utils/send.tsx","./models/instructions.ts","./models/serialisation.ts","./models/withcancelproposal.ts","./actions/cancelproposal.ts","./models/withcastvote.ts","./actions/castvote.ts","./models/withcreateproposal.ts","./models/withaddsignatory.ts","./models/withinsertinstruction.ts","./models/withsignoffproposal.ts","./actions/createproposal.ts","./utils/helpers.ts","./components/instructions/programs/bpfupgradeableloader.tsx","./node_modules/@project-serum/anchor/node_modules/@solana/web3.js/lib/index.d.ts","./node_modules/@project-serum/anchor/dist/cjs/provider.d.ts","./node_modules/@project-serum/anchor/dist/cjs/idl.d.ts","./node_modules/@project-serum/anchor/dist/cjs/coder/instruction.d.ts","./node_modules/@project-serum/anchor/dist/cjs/coder/accounts.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/common.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/context.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/namespace/types.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/event.d.ts","./node_modules/@project-serum/anchor/dist/cjs/coder/event.d.ts","./node_modules/@project-serum/anchor/dist/cjs/coder/state.d.ts","./node_modules/@project-serum/anchor/dist/cjs/coder/common.d.ts","./node_modules/@project-serum/anchor/dist/cjs/coder/index.d.ts","./node_modules/@project-serum/anchor/dist/cjs/error.d.ts","./node_modules/@project-serum/anchor/dist/cjs/utils/sha256.d.ts","./node_modules/@project-serum/anchor/dist/cjs/utils/rpc.d.ts","./node_modules/@project-serum/anchor/dist/cjs/utils/pubkey.d.ts","./node_modules/@project-serum/anchor/dist/cjs/utils/bytes/hex.d.ts","./node_modules/@project-serum/anchor/dist/cjs/utils/bytes/utf8.d.ts","./node_modules/@project-serum/anchor/dist/cjs/utils/bytes/bs58.d.ts","./node_modules/@project-serum/anchor/dist/cjs/utils/bytes/base64.d.ts","./node_modules/@project-serum/anchor/dist/cjs/utils/bytes/index.d.ts","./node_modules/@project-serum/anchor/dist/cjs/utils/token.d.ts","./node_modules/@project-serum/anchor/dist/cjs/utils/features.d.ts","./node_modules/@project-serum/anchor/dist/cjs/utils/index.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/namespace/state.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/namespace/instruction.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/namespace/transaction.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/namespace/rpc.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/namespace/account.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/namespace/simulate.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/namespace/index.d.ts","./node_modules/@project-serum/anchor/dist/cjs/program/index.d.ts","./node_modules/@project-serum/anchor/dist/cjs/index.d.ts","./tools/sdk/units.ts","./models/core/serialisation.ts","./tools/core/script.ts","./models/api.ts","./components/instructions/programs/governance.tsx","./node_modules/@blockworks-foundation/mango-client/lib/src/ids.json","./node_modules/@blockworks-foundation/mango-client/lib/src/mango_logs.json","./node_modules/@project-serum/serum/node_modules/@solana/web3.js/lib/index.d.ts","./node_modules/@project-serum/serum/lib/token-instructions.d.ts","./node_modules/@project-serum/serum/lib/error.d.ts","./node_modules/@project-serum/serum/lib/slab.d.ts","./node_modules/@project-serum/serum/lib/queue.d.ts","./node_modules/@project-serum/serum/lib/market.d.ts","./node_modules/@project-serum/serum/lib/layout.d.ts","./node_modules/@project-serum/serum/lib/instructions.d.ts","./node_modules/@project-serum/serum/lib/fees.d.ts","./node_modules/@project-serum/serum/lib/tokens_and_markets.d.ts","./node_modules/@project-serum/serum/lib/market-proxy/middleware.d.ts","./node_modules/@project-serum/serum/lib/market-proxy/index.d.ts","./node_modules/@project-serum/serum/lib/index.d.ts","./node_modules/@blockworks-foundation/mango-client/node_modules/@solana/web3.js/lib/index.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/fixednum.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/types.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/rootbank.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/mangogroup.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/book.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/perpmarket.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/perpaccount.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/layout.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/mangoaccount.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/perpeventqueue.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/client.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/config.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/instruction.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/token.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/utils.d.ts","./node_modules/@blockworks-foundation/mango-client/lib/src/index.d.ts","./components/instructions/programs/mango.tsx","./components/instructions/programs/names.ts","./components/instructions/programs/raydium.tsx","./components/instructions/programs/spltoken.tsx","./components/instructions/programs/uxdprotocol.tsx","./components/instructions/tools.tsx","./node_modules/axios/index.d.ts","./utils/services/token.tsx","./utils/tokens.tsx","./models/withcreatetokengovernance.ts","./models/withcreatespltokenaccount.ts","./actions/createtreasuryaccount.ts","./actions/dryruninstruction.ts","./models/withexecuteinstruction.ts","./actions/executeinstruction.ts","./models/withfinalizevote.ts","./actions/finalizevotes.ts","./models/withflaginstructionerror.ts","./actions/flaginstructionerror.ts","./models/withcreaterealm.ts","./actions/registerrealm.ts","./models/withrelinquishvote.ts","./actions/relinquishvote.ts","./actions/signoffproposal.ts","./models/chat/accounts.ts","./models/chat/instructions.ts","./models/chat/serialisation.ts","./models/chat/withpostchatmessage.ts","./actions/chat/postmessage.ts","./components/members/types.ts","./components/treasuryaccount/types.ts","./components/explorer/tools.ts","./utils/uitypes/proposalcreationtypes.ts","./node_modules/@solana/wallet-adapter-phantom/lib/adapter.d.ts","./node_modules/@solana/wallet-adapter-phantom/lib/index.d.ts","./node_modules/@solana/wallet-adapter-sollet/lib/adapter.d.ts","./node_modules/@solana/wallet-adapter-sollet/lib/index.d.ts","./utils/wallet-adapters/index.ts","./utils/github.ts","./models/chat/api.ts","./models/enums.ts","./public/realms/devnet.json","./public/realms/mainnet-beta.json","./models/types.ts","./utils/connection.ts","./tools/core/strings.ts","./models/registry/api.ts","./tools/core/pubkey.ts","./stores/usewalletstore.tsx","./node_modules/next/router.d.ts","./models/voteweights.ts","./hooks/userealm.tsx","./hooks/usegovernanceassets.ts","./hooks/useisbeyondtimestamp.ts","./hooks/usehasvotetimeexpired.ts","./hooks/useprevious.ts","./hooks/userealmgovernance.ts","./hooks/usequerycontext.tsx","./hooks/userouterhistory.ts","./models/createsetgovernanceconfig.ts","./models/createsetrealmconfig.ts","./models/errors.ts","./models/withcreateaccountgovernance.ts","./models/withcreatemintgovernance.ts","./models/withcreateprogramgovernance.ts","./models/withdepositgoverningtokens.ts","./models/withremoveinstruction.ts","./models/withsetrealmauthority.ts","./models/withwithdrawgoverningtokens.ts","./pages/api/hello.ts","./scripts/api.ts","./scripts/governance-notifier.ts","./node_modules/@types/aria-query/index.d.ts","./node_modules/@testing-library/dom/types/matches.d.ts","./node_modules/@testing-library/dom/types/wait-for.d.ts","./node_modules/@testing-library/dom/types/query-helpers.d.ts","./node_modules/@testing-library/dom/types/queries.d.ts","./node_modules/@testing-library/dom/types/get-queries-for-element.d.ts","./node_modules/pretty-format/build/types.d.ts","./node_modules/pretty-format/build/index.d.ts","./node_modules/@testing-library/dom/types/screen.d.ts","./node_modules/@testing-library/dom/types/wait.d.ts","./node_modules/@testing-library/dom/types/wait-for-dom-change.d.ts","./node_modules/@testing-library/dom/types/wait-for-element.d.ts","./node_modules/@testing-library/dom/types/wait-for-element-to-be-removed.d.ts","./node_modules/@testing-library/dom/types/get-node-text.d.ts","./node_modules/@testing-library/dom/types/events.d.ts","./node_modules/@testing-library/dom/types/pretty-dom.d.ts","./node_modules/@testing-library/dom/types/role-helpers.d.ts","./node_modules/@testing-library/dom/types/config.d.ts","./node_modules/@testing-library/dom/types/suggestions.d.ts","./node_modules/@testing-library/dom/types/index.d.ts","./node_modules/@testing-library/react/types/index.d.ts","./test/testutils.ts","./tools/routing.ts","./tools/core/option.ts","./tools/core/resources.ts","./tools/sdk/bpfupgradeableloader/createsetupgradeauthority.ts","./tools/sdk/bpfupgradeableloader/createupgradeinstruction.ts","./node_modules/@uxdprotocol/uxd-client/dist/types/controller.d.ts","./node_modules/@uxdprotocol/uxd-client/dist/types/mango_depository.d.ts","./node_modules/@uxdprotocol/uxd-client/dist/types/mango.d.ts","./node_modules/@uxdprotocol/uxd-client/dist/types/uxd.d.ts","./node_modules/@uxdprotocol/uxd-client/dist/types/uxd-interfaces.d.ts","./node_modules/@uxdprotocol/uxd-client/dist/types/uxd-helpers.d.ts","./node_modules/@uxdprotocol/uxd-client/dist/types/utils.d.ts","./node_modules/@uxdprotocol/uxd-client/dist/types/uxd-client.d.ts","./tools/sdk/uxdprotocol/uxdidl.ts","./tools/sdk/uxdprotocol/uxdclient.ts","./tools/sdk/uxdprotocol/createdepositinsurancetomangodepositoryinstruction.ts","./tools/sdk/uxdprotocol/createinitializecontrollerinstruction.ts","./tools/sdk/uxdprotocol/createregistermangodepositoryinstruction.ts","./tools/sdk/uxdprotocol/createsetmangodepositoriesredeemablesoftcapinstruction.ts","./tools/sdk/uxdprotocol/createsetredeemableglobalsupplycapinstruction.ts","./tools/sdk/uxdprotocol/createwithdrawinsurancefrommangodepositoryinstruction.ts","./node_modules/superstruct/lib/error.d.ts","./node_modules/superstruct/lib/utils.d.ts","./node_modules/superstruct/lib/struct.d.ts","./node_modules/superstruct/lib/structs/coercions.d.ts","./node_modules/superstruct/lib/structs/refinements.d.ts","./node_modules/superstruct/lib/structs/types.d.ts","./node_modules/superstruct/lib/structs/utilities.d.ts","./node_modules/superstruct/lib/index.d.ts","./tools/validators/pubkey.ts","./tools/validators/accounts/token.ts","./tools/validators/accounts/upgradeable-program.ts","./node_modules/yup/lib/reference.d.ts","./node_modules/yup/lib/condition.d.ts","./node_modules/yup/lib/validationerror.d.ts","./node_modules/yup/lib/util/createvalidation.d.ts","./node_modules/yup/lib/util/types.d.ts","./node_modules/yup/lib/util/referenceset.d.ts","./node_modules/yup/lib/schema.d.ts","./node_modules/yup/lib/lazy.d.ts","./node_modules/yup/lib/types.d.ts","./node_modules/yup/lib/locale.d.ts","./node_modules/yup/lib/mixed.d.ts","./node_modules/yup/lib/boolean.d.ts","./node_modules/yup/lib/string.d.ts","./node_modules/yup/lib/number.d.ts","./node_modules/yup/lib/date.d.ts","./node_modules/yup/lib/object.d.ts","./node_modules/yup/lib/array.d.ts","./node_modules/yup/lib/util/reach.d.ts","./node_modules/yup/lib/util/isschema.d.ts","./node_modules/yup/lib/setlocale.d.ts","./node_modules/yup/lib/index.d.ts","./utils/validations.tsx","./utils/atatools.tsx","./utils/formvalidation.tsx","./utils/instructiontools.ts","./node_modules/@popperjs/core/lib/enums.d.ts","./node_modules/@popperjs/core/lib/modifiers/popperoffsets.d.ts","./node_modules/@popperjs/core/lib/modifiers/flip.d.ts","./node_modules/@popperjs/core/lib/modifiers/hide.d.ts","./node_modules/@popperjs/core/lib/modifiers/offset.d.ts","./node_modules/@popperjs/core/lib/modifiers/eventlisteners.d.ts","./node_modules/@popperjs/core/lib/modifiers/computestyles.d.ts","./node_modules/@popperjs/core/lib/modifiers/arrow.d.ts","./node_modules/@popperjs/core/lib/modifiers/preventoverflow.d.ts","./node_modules/@popperjs/core/lib/modifiers/applystyles.d.ts","./node_modules/@popperjs/core/lib/types.d.ts","./node_modules/@popperjs/core/lib/modifiers/index.d.ts","./node_modules/@popperjs/core/lib/utils/detectoverflow.d.ts","./node_modules/@popperjs/core/lib/createpopper.d.ts","./node_modules/@popperjs/core/lib/popper-lite.d.ts","./node_modules/@popperjs/core/lib/popper.d.ts","./node_modules/@popperjs/core/lib/index.d.ts","./node_modules/@popperjs/core/index.d.ts","./node_modules/tippy.js/index.d.ts","./node_modules/@tippyjs/react/index.d.ts","./components/tooltip.tsx","./components/approvalquorum.tsx","./components/loading.tsx","./components/button.tsx","./node_modules/@heroicons/react/outline/academiccapicon.d.ts","./node_modules/@heroicons/react/outline/adjustmentsicon.d.ts","./node_modules/@heroicons/react/outline/annotationicon.d.ts","./node_modules/@heroicons/react/outline/archiveicon.d.ts","./node_modules/@heroicons/react/outline/arrowcircledownicon.d.ts","./node_modules/@heroicons/react/outline/arrowcirclelefticon.d.ts","./node_modules/@heroicons/react/outline/arrowcirclerighticon.d.ts","./node_modules/@heroicons/react/outline/arrowcircleupicon.d.ts","./node_modules/@heroicons/react/outline/arrowdownicon.d.ts","./node_modules/@heroicons/react/outline/arrowlefticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowdownicon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowlefticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowrighticon.d.ts","./node_modules/@heroicons/react/outline/arrownarrowupicon.d.ts","./node_modules/@heroicons/react/outline/arrowrighticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmdownicon.d.ts","./node_modules/@heroicons/react/outline/arrowsmlefticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmrighticon.d.ts","./node_modules/@heroicons/react/outline/arrowsmupicon.d.ts","./node_modules/@heroicons/react/outline/arrowupicon.d.ts","./node_modules/@heroicons/react/outline/arrowsexpandicon.d.ts","./node_modules/@heroicons/react/outline/atsymbolicon.d.ts","./node_modules/@heroicons/react/outline/backspaceicon.d.ts","./node_modules/@heroicons/react/outline/badgecheckicon.d.ts","./node_modules/@heroicons/react/outline/banicon.d.ts","./node_modules/@heroicons/react/outline/beakericon.d.ts","./node_modules/@heroicons/react/outline/bellicon.d.ts","./node_modules/@heroicons/react/outline/bookopenicon.d.ts","./node_modules/@heroicons/react/outline/bookmarkalticon.d.ts","./node_modules/@heroicons/react/outline/bookmarkicon.d.ts","./node_modules/@heroicons/react/outline/briefcaseicon.d.ts","./node_modules/@heroicons/react/outline/cakeicon.d.ts","./node_modules/@heroicons/react/outline/calculatoricon.d.ts","./node_modules/@heroicons/react/outline/calendaricon.d.ts","./node_modules/@heroicons/react/outline/cameraicon.d.ts","./node_modules/@heroicons/react/outline/cashicon.d.ts","./node_modules/@heroicons/react/outline/chartbaricon.d.ts","./node_modules/@heroicons/react/outline/chartpieicon.d.ts","./node_modules/@heroicons/react/outline/chartsquarebaricon.d.ts","./node_modules/@heroicons/react/outline/chatalt2icon.d.ts","./node_modules/@heroicons/react/outline/chatalticon.d.ts","./node_modules/@heroicons/react/outline/chaticon.d.ts","./node_modules/@heroicons/react/outline/checkcircleicon.d.ts","./node_modules/@heroicons/react/outline/checkicon.d.ts","./node_modules/@heroicons/react/outline/chevrondoubledownicon.d.ts","./node_modules/@heroicons/react/outline/chevrondoublelefticon.d.ts","./node_modules/@heroicons/react/outline/chevrondoublerighticon.d.ts","./node_modules/@heroicons/react/outline/chevrondoubleupicon.d.ts","./node_modules/@heroicons/react/outline/chevrondownicon.d.ts","./node_modules/@heroicons/react/outline/chevronlefticon.d.ts","./node_modules/@heroicons/react/outline/chevronrighticon.d.ts","./node_modules/@heroicons/react/outline/chevronupicon.d.ts","./node_modules/@heroicons/react/outline/chipicon.d.ts","./node_modules/@heroicons/react/outline/clipboardcheckicon.d.ts","./node_modules/@heroicons/react/outline/clipboardcopyicon.d.ts","./node_modules/@heroicons/react/outline/clipboardlisticon.d.ts","./node_modules/@heroicons/react/outline/clipboardicon.d.ts","./node_modules/@heroicons/react/outline/clockicon.d.ts","./node_modules/@heroicons/react/outline/clouddownloadicon.d.ts","./node_modules/@heroicons/react/outline/clouduploadicon.d.ts","./node_modules/@heroicons/react/outline/cloudicon.d.ts","./node_modules/@heroicons/react/outline/codeicon.d.ts","./node_modules/@heroicons/react/outline/cogicon.d.ts","./node_modules/@heroicons/react/outline/collectionicon.d.ts","./node_modules/@heroicons/react/outline/colorswatchicon.d.ts","./node_modules/@heroicons/react/outline/creditcardicon.d.ts","./node_modules/@heroicons/react/outline/cubetransparenticon.d.ts","./node_modules/@heroicons/react/outline/cubeicon.d.ts","./node_modules/@heroicons/react/outline/currencybangladeshiicon.d.ts","./node_modules/@heroicons/react/outline/currencydollaricon.d.ts","./node_modules/@heroicons/react/outline/currencyeuroicon.d.ts","./node_modules/@heroicons/react/outline/currencypoundicon.d.ts","./node_modules/@heroicons/react/outline/currencyrupeeicon.d.ts","./node_modules/@heroicons/react/outline/currencyyenicon.d.ts","./node_modules/@heroicons/react/outline/cursorclickicon.d.ts","./node_modules/@heroicons/react/outline/databaseicon.d.ts","./node_modules/@heroicons/react/outline/desktopcomputericon.d.ts","./node_modules/@heroicons/react/outline/devicemobileicon.d.ts","./node_modules/@heroicons/react/outline/devicetableticon.d.ts","./node_modules/@heroicons/react/outline/documentaddicon.d.ts","./node_modules/@heroicons/react/outline/documentdownloadicon.d.ts","./node_modules/@heroicons/react/outline/documentduplicateicon.d.ts","./node_modules/@heroicons/react/outline/documentremoveicon.d.ts","./node_modules/@heroicons/react/outline/documentreporticon.d.ts","./node_modules/@heroicons/react/outline/documentsearchicon.d.ts","./node_modules/@heroicons/react/outline/documenttexticon.d.ts","./node_modules/@heroicons/react/outline/documenticon.d.ts","./node_modules/@heroicons/react/outline/dotscirclehorizontalicon.d.ts","./node_modules/@heroicons/react/outline/dotshorizontalicon.d.ts","./node_modules/@heroicons/react/outline/dotsverticalicon.d.ts","./node_modules/@heroicons/react/outline/downloadicon.d.ts","./node_modules/@heroicons/react/outline/duplicateicon.d.ts","./node_modules/@heroicons/react/outline/emojihappyicon.d.ts","./node_modules/@heroicons/react/outline/emojisadicon.d.ts","./node_modules/@heroicons/react/outline/exclamationcircleicon.d.ts","./node_modules/@heroicons/react/outline/exclamationicon.d.ts","./node_modules/@heroicons/react/outline/externallinkicon.d.ts","./node_modules/@heroicons/react/outline/eyeofficon.d.ts","./node_modules/@heroicons/react/outline/eyeicon.d.ts","./node_modules/@heroicons/react/outline/fastforwardicon.d.ts","./node_modules/@heroicons/react/outline/filmicon.d.ts","./node_modules/@heroicons/react/outline/filtericon.d.ts","./node_modules/@heroicons/react/outline/fingerprinticon.d.ts","./node_modules/@heroicons/react/outline/fireicon.d.ts","./node_modules/@heroicons/react/outline/flagicon.d.ts","./node_modules/@heroicons/react/outline/folderaddicon.d.ts","./node_modules/@heroicons/react/outline/folderdownloadicon.d.ts","./node_modules/@heroicons/react/outline/folderopenicon.d.ts","./node_modules/@heroicons/react/outline/folderremoveicon.d.ts","./node_modules/@heroicons/react/outline/foldericon.d.ts","./node_modules/@heroicons/react/outline/gifticon.d.ts","./node_modules/@heroicons/react/outline/globealticon.d.ts","./node_modules/@heroicons/react/outline/globeicon.d.ts","./node_modules/@heroicons/react/outline/handicon.d.ts","./node_modules/@heroicons/react/outline/hashtagicon.d.ts","./node_modules/@heroicons/react/outline/hearticon.d.ts","./node_modules/@heroicons/react/outline/homeicon.d.ts","./node_modules/@heroicons/react/outline/identificationicon.d.ts","./node_modules/@heroicons/react/outline/inboxinicon.d.ts","./node_modules/@heroicons/react/outline/inboxicon.d.ts","./node_modules/@heroicons/react/outline/informationcircleicon.d.ts","./node_modules/@heroicons/react/outline/keyicon.d.ts","./node_modules/@heroicons/react/outline/libraryicon.d.ts","./node_modules/@heroicons/react/outline/lightbulbicon.d.ts","./node_modules/@heroicons/react/outline/lightningbolticon.d.ts","./node_modules/@heroicons/react/outline/linkicon.d.ts","./node_modules/@heroicons/react/outline/locationmarkericon.d.ts","./node_modules/@heroicons/react/outline/lockclosedicon.d.ts","./node_modules/@heroicons/react/outline/lockopenicon.d.ts","./node_modules/@heroicons/react/outline/loginicon.d.ts","./node_modules/@heroicons/react/outline/logouticon.d.ts","./node_modules/@heroicons/react/outline/mailopenicon.d.ts","./node_modules/@heroicons/react/outline/mailicon.d.ts","./node_modules/@heroicons/react/outline/mapicon.d.ts","./node_modules/@heroicons/react/outline/menualt1icon.d.ts","./node_modules/@heroicons/react/outline/menualt2icon.d.ts","./node_modules/@heroicons/react/outline/menualt3icon.d.ts","./node_modules/@heroicons/react/outline/menualt4icon.d.ts","./node_modules/@heroicons/react/outline/menuicon.d.ts","./node_modules/@heroicons/react/outline/microphoneicon.d.ts","./node_modules/@heroicons/react/outline/minuscircleicon.d.ts","./node_modules/@heroicons/react/outline/minussmicon.d.ts","./node_modules/@heroicons/react/outline/minusicon.d.ts","./node_modules/@heroicons/react/outline/moonicon.d.ts","./node_modules/@heroicons/react/outline/musicnoteicon.d.ts","./node_modules/@heroicons/react/outline/newspapericon.d.ts","./node_modules/@heroicons/react/outline/officebuildingicon.d.ts","./node_modules/@heroicons/react/outline/paperairplaneicon.d.ts","./node_modules/@heroicons/react/outline/paperclipicon.d.ts","./node_modules/@heroicons/react/outline/pauseicon.d.ts","./node_modules/@heroicons/react/outline/pencilalticon.d.ts","./node_modules/@heroicons/react/outline/pencilicon.d.ts","./node_modules/@heroicons/react/outline/phoneincomingicon.d.ts","./node_modules/@heroicons/react/outline/phonemissedcallicon.d.ts","./node_modules/@heroicons/react/outline/phoneoutgoingicon.d.ts","./node_modules/@heroicons/react/outline/phoneicon.d.ts","./node_modules/@heroicons/react/outline/photographicon.d.ts","./node_modules/@heroicons/react/outline/playicon.d.ts","./node_modules/@heroicons/react/outline/pluscircleicon.d.ts","./node_modules/@heroicons/react/outline/plussmicon.d.ts","./node_modules/@heroicons/react/outline/plusicon.d.ts","./node_modules/@heroicons/react/outline/presentationchartbaricon.d.ts","./node_modules/@heroicons/react/outline/presentationchartlineicon.d.ts","./node_modules/@heroicons/react/outline/printericon.d.ts","./node_modules/@heroicons/react/outline/puzzleicon.d.ts","./node_modules/@heroicons/react/outline/qrcodeicon.d.ts","./node_modules/@heroicons/react/outline/questionmarkcircleicon.d.ts","./node_modules/@heroicons/react/outline/receiptrefundicon.d.ts","./node_modules/@heroicons/react/outline/receipttaxicon.d.ts","./node_modules/@heroicons/react/outline/refreshicon.d.ts","./node_modules/@heroicons/react/outline/replyicon.d.ts","./node_modules/@heroicons/react/outline/rewindicon.d.ts","./node_modules/@heroicons/react/outline/rssicon.d.ts","./node_modules/@heroicons/react/outline/saveasicon.d.ts","./node_modules/@heroicons/react/outline/saveicon.d.ts","./node_modules/@heroicons/react/outline/scaleicon.d.ts","./node_modules/@heroicons/react/outline/scissorsicon.d.ts","./node_modules/@heroicons/react/outline/searchcircleicon.d.ts","./node_modules/@heroicons/react/outline/searchicon.d.ts","./node_modules/@heroicons/react/outline/selectoricon.d.ts","./node_modules/@heroicons/react/outline/servericon.d.ts","./node_modules/@heroicons/react/outline/shareicon.d.ts","./node_modules/@heroicons/react/outline/shieldcheckicon.d.ts","./node_modules/@heroicons/react/outline/shieldexclamationicon.d.ts","./node_modules/@heroicons/react/outline/shoppingbagicon.d.ts","./node_modules/@heroicons/react/outline/shoppingcarticon.d.ts","./node_modules/@heroicons/react/outline/sortascendingicon.d.ts","./node_modules/@heroicons/react/outline/sortdescendingicon.d.ts","./node_modules/@heroicons/react/outline/sparklesicon.d.ts","./node_modules/@heroicons/react/outline/speakerphoneicon.d.ts","./node_modules/@heroicons/react/outline/staricon.d.ts","./node_modules/@heroicons/react/outline/statusofflineicon.d.ts","./node_modules/@heroicons/react/outline/statusonlineicon.d.ts","./node_modules/@heroicons/react/outline/stopicon.d.ts","./node_modules/@heroicons/react/outline/sunicon.d.ts","./node_modules/@heroicons/react/outline/supporticon.d.ts","./node_modules/@heroicons/react/outline/switchhorizontalicon.d.ts","./node_modules/@heroicons/react/outline/switchverticalicon.d.ts","./node_modules/@heroicons/react/outline/tableicon.d.ts","./node_modules/@heroicons/react/outline/tagicon.d.ts","./node_modules/@heroicons/react/outline/templateicon.d.ts","./node_modules/@heroicons/react/outline/terminalicon.d.ts","./node_modules/@heroicons/react/outline/thumbdownicon.d.ts","./node_modules/@heroicons/react/outline/thumbupicon.d.ts","./node_modules/@heroicons/react/outline/ticketicon.d.ts","./node_modules/@heroicons/react/outline/translateicon.d.ts","./node_modules/@heroicons/react/outline/trashicon.d.ts","./node_modules/@heroicons/react/outline/trendingdownicon.d.ts","./node_modules/@heroicons/react/outline/trendingupicon.d.ts","./node_modules/@heroicons/react/outline/truckicon.d.ts","./node_modules/@heroicons/react/outline/uploadicon.d.ts","./node_modules/@heroicons/react/outline/useraddicon.d.ts","./node_modules/@heroicons/react/outline/usercircleicon.d.ts","./node_modules/@heroicons/react/outline/usergroupicon.d.ts","./node_modules/@heroicons/react/outline/userremoveicon.d.ts","./node_modules/@heroicons/react/outline/usericon.d.ts","./node_modules/@heroicons/react/outline/usersicon.d.ts","./node_modules/@heroicons/react/outline/variableicon.d.ts","./node_modules/@heroicons/react/outline/videocameraicon.d.ts","./node_modules/@heroicons/react/outline/viewboardsicon.d.ts","./node_modules/@heroicons/react/outline/viewgridaddicon.d.ts","./node_modules/@heroicons/react/outline/viewgridicon.d.ts","./node_modules/@heroicons/react/outline/viewlisticon.d.ts","./node_modules/@heroicons/react/outline/volumeofficon.d.ts","./node_modules/@heroicons/react/outline/volumeupicon.d.ts","./node_modules/@heroicons/react/outline/wifiicon.d.ts","./node_modules/@heroicons/react/outline/xcircleicon.d.ts","./node_modules/@heroicons/react/outline/xicon.d.ts","./node_modules/@heroicons/react/outline/zoominicon.d.ts","./node_modules/@heroicons/react/outline/zoomouticon.d.ts","./node_modules/@heroicons/react/outline/index.d.ts","./components/modal.tsx","./hooks/useproposal.tsx","./components/cancelproposalmodal.tsx","./node_modules/@headlessui/react/dist/types.d.ts","./node_modules/@headlessui/react/dist/utils/render.d.ts","./node_modules/@headlessui/react/dist/components/description/description.d.ts","./node_modules/@headlessui/react/dist/components/dialog/dialog.d.ts","./node_modules/@headlessui/react/dist/components/disclosure/disclosure.d.ts","./node_modules/@headlessui/react/dist/components/focus-trap/focus-trap.d.ts","./node_modules/@headlessui/react/dist/components/listbox/listbox.d.ts","./node_modules/@headlessui/react/dist/components/menu/menu.d.ts","./node_modules/@headlessui/react/dist/components/popover/popover.d.ts","./node_modules/@headlessui/react/dist/components/portal/portal.d.ts","./node_modules/@headlessui/react/dist/components/label/label.d.ts","./node_modules/@headlessui/react/dist/components/radio-group/radio-group.d.ts","./node_modules/@headlessui/react/dist/components/switch/switch.d.ts","./node_modules/@headlessui/react/dist/components/transitions/transition.d.ts","./node_modules/@headlessui/react/dist/index.d.ts","./node_modules/@heroicons/react/solid/academiccapicon.d.ts","./node_modules/@heroicons/react/solid/adjustmentsicon.d.ts","./node_modules/@heroicons/react/solid/annotationicon.d.ts","./node_modules/@heroicons/react/solid/archiveicon.d.ts","./node_modules/@heroicons/react/solid/arrowcircledownicon.d.ts","./node_modules/@heroicons/react/solid/arrowcirclelefticon.d.ts","./node_modules/@heroicons/react/solid/arrowcirclerighticon.d.ts","./node_modules/@heroicons/react/solid/arrowcircleupicon.d.ts","./node_modules/@heroicons/react/solid/arrowdownicon.d.ts","./node_modules/@heroicons/react/solid/arrowlefticon.d.ts","./node_modules/@heroicons/react/solid/arrownarrowdownicon.d.ts","./node_modules/@heroicons/react/solid/arrownarrowlefticon.d.ts","./node_modules/@heroicons/react/solid/arrownarrowrighticon.d.ts","./node_modules/@heroicons/react/solid/arrownarrowupicon.d.ts","./node_modules/@heroicons/react/solid/arrowrighticon.d.ts","./node_modules/@heroicons/react/solid/arrowsmdownicon.d.ts","./node_modules/@heroicons/react/solid/arrowsmlefticon.d.ts","./node_modules/@heroicons/react/solid/arrowsmrighticon.d.ts","./node_modules/@heroicons/react/solid/arrowsmupicon.d.ts","./node_modules/@heroicons/react/solid/arrowupicon.d.ts","./node_modules/@heroicons/react/solid/arrowsexpandicon.d.ts","./node_modules/@heroicons/react/solid/atsymbolicon.d.ts","./node_modules/@heroicons/react/solid/backspaceicon.d.ts","./node_modules/@heroicons/react/solid/badgecheckicon.d.ts","./node_modules/@heroicons/react/solid/banicon.d.ts","./node_modules/@heroicons/react/solid/beakericon.d.ts","./node_modules/@heroicons/react/solid/bellicon.d.ts","./node_modules/@heroicons/react/solid/bookopenicon.d.ts","./node_modules/@heroicons/react/solid/bookmarkalticon.d.ts","./node_modules/@heroicons/react/solid/bookmarkicon.d.ts","./node_modules/@heroicons/react/solid/briefcaseicon.d.ts","./node_modules/@heroicons/react/solid/cakeicon.d.ts","./node_modules/@heroicons/react/solid/calculatoricon.d.ts","./node_modules/@heroicons/react/solid/calendaricon.d.ts","./node_modules/@heroicons/react/solid/cameraicon.d.ts","./node_modules/@heroicons/react/solid/cashicon.d.ts","./node_modules/@heroicons/react/solid/chartbaricon.d.ts","./node_modules/@heroicons/react/solid/chartpieicon.d.ts","./node_modules/@heroicons/react/solid/chartsquarebaricon.d.ts","./node_modules/@heroicons/react/solid/chatalt2icon.d.ts","./node_modules/@heroicons/react/solid/chatalticon.d.ts","./node_modules/@heroicons/react/solid/chaticon.d.ts","./node_modules/@heroicons/react/solid/checkcircleicon.d.ts","./node_modules/@heroicons/react/solid/checkicon.d.ts","./node_modules/@heroicons/react/solid/chevrondoubledownicon.d.ts","./node_modules/@heroicons/react/solid/chevrondoublelefticon.d.ts","./node_modules/@heroicons/react/solid/chevrondoublerighticon.d.ts","./node_modules/@heroicons/react/solid/chevrondoubleupicon.d.ts","./node_modules/@heroicons/react/solid/chevrondownicon.d.ts","./node_modules/@heroicons/react/solid/chevronlefticon.d.ts","./node_modules/@heroicons/react/solid/chevronrighticon.d.ts","./node_modules/@heroicons/react/solid/chevronupicon.d.ts","./node_modules/@heroicons/react/solid/chipicon.d.ts","./node_modules/@heroicons/react/solid/clipboardcheckicon.d.ts","./node_modules/@heroicons/react/solid/clipboardcopyicon.d.ts","./node_modules/@heroicons/react/solid/clipboardlisticon.d.ts","./node_modules/@heroicons/react/solid/clipboardicon.d.ts","./node_modules/@heroicons/react/solid/clockicon.d.ts","./node_modules/@heroicons/react/solid/clouddownloadicon.d.ts","./node_modules/@heroicons/react/solid/clouduploadicon.d.ts","./node_modules/@heroicons/react/solid/cloudicon.d.ts","./node_modules/@heroicons/react/solid/codeicon.d.ts","./node_modules/@heroicons/react/solid/cogicon.d.ts","./node_modules/@heroicons/react/solid/collectionicon.d.ts","./node_modules/@heroicons/react/solid/colorswatchicon.d.ts","./node_modules/@heroicons/react/solid/creditcardicon.d.ts","./node_modules/@heroicons/react/solid/cubetransparenticon.d.ts","./node_modules/@heroicons/react/solid/cubeicon.d.ts","./node_modules/@heroicons/react/solid/currencybangladeshiicon.d.ts","./node_modules/@heroicons/react/solid/currencydollaricon.d.ts","./node_modules/@heroicons/react/solid/currencyeuroicon.d.ts","./node_modules/@heroicons/react/solid/currencypoundicon.d.ts","./node_modules/@heroicons/react/solid/currencyrupeeicon.d.ts","./node_modules/@heroicons/react/solid/currencyyenicon.d.ts","./node_modules/@heroicons/react/solid/cursorclickicon.d.ts","./node_modules/@heroicons/react/solid/databaseicon.d.ts","./node_modules/@heroicons/react/solid/desktopcomputericon.d.ts","./node_modules/@heroicons/react/solid/devicemobileicon.d.ts","./node_modules/@heroicons/react/solid/devicetableticon.d.ts","./node_modules/@heroicons/react/solid/documentaddicon.d.ts","./node_modules/@heroicons/react/solid/documentdownloadicon.d.ts","./node_modules/@heroicons/react/solid/documentduplicateicon.d.ts","./node_modules/@heroicons/react/solid/documentremoveicon.d.ts","./node_modules/@heroicons/react/solid/documentreporticon.d.ts","./node_modules/@heroicons/react/solid/documentsearchicon.d.ts","./node_modules/@heroicons/react/solid/documenttexticon.d.ts","./node_modules/@heroicons/react/solid/documenticon.d.ts","./node_modules/@heroicons/react/solid/dotscirclehorizontalicon.d.ts","./node_modules/@heroicons/react/solid/dotshorizontalicon.d.ts","./node_modules/@heroicons/react/solid/dotsverticalicon.d.ts","./node_modules/@heroicons/react/solid/downloadicon.d.ts","./node_modules/@heroicons/react/solid/duplicateicon.d.ts","./node_modules/@heroicons/react/solid/emojihappyicon.d.ts","./node_modules/@heroicons/react/solid/emojisadicon.d.ts","./node_modules/@heroicons/react/solid/exclamationcircleicon.d.ts","./node_modules/@heroicons/react/solid/exclamationicon.d.ts","./node_modules/@heroicons/react/solid/externallinkicon.d.ts","./node_modules/@heroicons/react/solid/eyeofficon.d.ts","./node_modules/@heroicons/react/solid/eyeicon.d.ts","./node_modules/@heroicons/react/solid/fastforwardicon.d.ts","./node_modules/@heroicons/react/solid/filmicon.d.ts","./node_modules/@heroicons/react/solid/filtericon.d.ts","./node_modules/@heroicons/react/solid/fingerprinticon.d.ts","./node_modules/@heroicons/react/solid/fireicon.d.ts","./node_modules/@heroicons/react/solid/flagicon.d.ts","./node_modules/@heroicons/react/solid/folderaddicon.d.ts","./node_modules/@heroicons/react/solid/folderdownloadicon.d.ts","./node_modules/@heroicons/react/solid/folderopenicon.d.ts","./node_modules/@heroicons/react/solid/folderremoveicon.d.ts","./node_modules/@heroicons/react/solid/foldericon.d.ts","./node_modules/@heroicons/react/solid/gifticon.d.ts","./node_modules/@heroicons/react/solid/globealticon.d.ts","./node_modules/@heroicons/react/solid/globeicon.d.ts","./node_modules/@heroicons/react/solid/handicon.d.ts","./node_modules/@heroicons/react/solid/hashtagicon.d.ts","./node_modules/@heroicons/react/solid/hearticon.d.ts","./node_modules/@heroicons/react/solid/homeicon.d.ts","./node_modules/@heroicons/react/solid/identificationicon.d.ts","./node_modules/@heroicons/react/solid/inboxinicon.d.ts","./node_modules/@heroicons/react/solid/inboxicon.d.ts","./node_modules/@heroicons/react/solid/informationcircleicon.d.ts","./node_modules/@heroicons/react/solid/keyicon.d.ts","./node_modules/@heroicons/react/solid/libraryicon.d.ts","./node_modules/@heroicons/react/solid/lightbulbicon.d.ts","./node_modules/@heroicons/react/solid/lightningbolticon.d.ts","./node_modules/@heroicons/react/solid/linkicon.d.ts","./node_modules/@heroicons/react/solid/locationmarkericon.d.ts","./node_modules/@heroicons/react/solid/lockclosedicon.d.ts","./node_modules/@heroicons/react/solid/lockopenicon.d.ts","./node_modules/@heroicons/react/solid/loginicon.d.ts","./node_modules/@heroicons/react/solid/logouticon.d.ts","./node_modules/@heroicons/react/solid/mailopenicon.d.ts","./node_modules/@heroicons/react/solid/mailicon.d.ts","./node_modules/@heroicons/react/solid/mapicon.d.ts","./node_modules/@heroicons/react/solid/menualt1icon.d.ts","./node_modules/@heroicons/react/solid/menualt2icon.d.ts","./node_modules/@heroicons/react/solid/menualt3icon.d.ts","./node_modules/@heroicons/react/solid/menualt4icon.d.ts","./node_modules/@heroicons/react/solid/menuicon.d.ts","./node_modules/@heroicons/react/solid/microphoneicon.d.ts","./node_modules/@heroicons/react/solid/minuscircleicon.d.ts","./node_modules/@heroicons/react/solid/minussmicon.d.ts","./node_modules/@heroicons/react/solid/minusicon.d.ts","./node_modules/@heroicons/react/solid/moonicon.d.ts","./node_modules/@heroicons/react/solid/musicnoteicon.d.ts","./node_modules/@heroicons/react/solid/newspapericon.d.ts","./node_modules/@heroicons/react/solid/officebuildingicon.d.ts","./node_modules/@heroicons/react/solid/paperairplaneicon.d.ts","./node_modules/@heroicons/react/solid/paperclipicon.d.ts","./node_modules/@heroicons/react/solid/pauseicon.d.ts","./node_modules/@heroicons/react/solid/pencilalticon.d.ts","./node_modules/@heroicons/react/solid/pencilicon.d.ts","./node_modules/@heroicons/react/solid/phoneincomingicon.d.ts","./node_modules/@heroicons/react/solid/phonemissedcallicon.d.ts","./node_modules/@heroicons/react/solid/phoneoutgoingicon.d.ts","./node_modules/@heroicons/react/solid/phoneicon.d.ts","./node_modules/@heroicons/react/solid/photographicon.d.ts","./node_modules/@heroicons/react/solid/playicon.d.ts","./node_modules/@heroicons/react/solid/pluscircleicon.d.ts","./node_modules/@heroicons/react/solid/plussmicon.d.ts","./node_modules/@heroicons/react/solid/plusicon.d.ts","./node_modules/@heroicons/react/solid/presentationchartbaricon.d.ts","./node_modules/@heroicons/react/solid/presentationchartlineicon.d.ts","./node_modules/@heroicons/react/solid/printericon.d.ts","./node_modules/@heroicons/react/solid/puzzleicon.d.ts","./node_modules/@heroicons/react/solid/qrcodeicon.d.ts","./node_modules/@heroicons/react/solid/questionmarkcircleicon.d.ts","./node_modules/@heroicons/react/solid/receiptrefundicon.d.ts","./node_modules/@heroicons/react/solid/receipttaxicon.d.ts","./node_modules/@heroicons/react/solid/refreshicon.d.ts","./node_modules/@heroicons/react/solid/replyicon.d.ts","./node_modules/@heroicons/react/solid/rewindicon.d.ts","./node_modules/@heroicons/react/solid/rssicon.d.ts","./node_modules/@heroicons/react/solid/saveasicon.d.ts","./node_modules/@heroicons/react/solid/saveicon.d.ts","./node_modules/@heroicons/react/solid/scaleicon.d.ts","./node_modules/@heroicons/react/solid/scissorsicon.d.ts","./node_modules/@heroicons/react/solid/searchcircleicon.d.ts","./node_modules/@heroicons/react/solid/searchicon.d.ts","./node_modules/@heroicons/react/solid/selectoricon.d.ts","./node_modules/@heroicons/react/solid/servericon.d.ts","./node_modules/@heroicons/react/solid/shareicon.d.ts","./node_modules/@heroicons/react/solid/shieldcheckicon.d.ts","./node_modules/@heroicons/react/solid/shieldexclamationicon.d.ts","./node_modules/@heroicons/react/solid/shoppingbagicon.d.ts","./node_modules/@heroicons/react/solid/shoppingcarticon.d.ts","./node_modules/@heroicons/react/solid/sortascendingicon.d.ts","./node_modules/@heroicons/react/solid/sortdescendingicon.d.ts","./node_modules/@heroicons/react/solid/sparklesicon.d.ts","./node_modules/@heroicons/react/solid/speakerphoneicon.d.ts","./node_modules/@heroicons/react/solid/staricon.d.ts","./node_modules/@heroicons/react/solid/statusofflineicon.d.ts","./node_modules/@heroicons/react/solid/statusonlineicon.d.ts","./node_modules/@heroicons/react/solid/stopicon.d.ts","./node_modules/@heroicons/react/solid/sunicon.d.ts","./node_modules/@heroicons/react/solid/supporticon.d.ts","./node_modules/@heroicons/react/solid/switchhorizontalicon.d.ts","./node_modules/@heroicons/react/solid/switchverticalicon.d.ts","./node_modules/@heroicons/react/solid/tableicon.d.ts","./node_modules/@heroicons/react/solid/tagicon.d.ts","./node_modules/@heroicons/react/solid/templateicon.d.ts","./node_modules/@heroicons/react/solid/terminalicon.d.ts","./node_modules/@heroicons/react/solid/thumbdownicon.d.ts","./node_modules/@heroicons/react/solid/thumbupicon.d.ts","./node_modules/@heroicons/react/solid/ticketicon.d.ts","./node_modules/@heroicons/react/solid/translateicon.d.ts","./node_modules/@heroicons/react/solid/trashicon.d.ts","./node_modules/@heroicons/react/solid/trendingdownicon.d.ts","./node_modules/@heroicons/react/solid/trendingupicon.d.ts","./node_modules/@heroicons/react/solid/truckicon.d.ts","./node_modules/@heroicons/react/solid/uploadicon.d.ts","./node_modules/@heroicons/react/solid/useraddicon.d.ts","./node_modules/@heroicons/react/solid/usercircleicon.d.ts","./node_modules/@heroicons/react/solid/usergroupicon.d.ts","./node_modules/@heroicons/react/solid/userremoveicon.d.ts","./node_modules/@heroicons/react/solid/usericon.d.ts","./node_modules/@heroicons/react/solid/usersicon.d.ts","./node_modules/@heroicons/react/solid/variableicon.d.ts","./node_modules/@heroicons/react/solid/videocameraicon.d.ts","./node_modules/@heroicons/react/solid/viewboardsicon.d.ts","./node_modules/@heroicons/react/solid/viewgridaddicon.d.ts","./node_modules/@heroicons/react/solid/viewgridicon.d.ts","./node_modules/@heroicons/react/solid/viewlisticon.d.ts","./node_modules/@heroicons/react/solid/volumeofficon.d.ts","./node_modules/@heroicons/react/solid/volumeupicon.d.ts","./node_modules/@heroicons/react/solid/wifiicon.d.ts","./node_modules/@heroicons/react/solid/xcircleicon.d.ts","./node_modules/@heroicons/react/solid/xicon.d.ts","./node_modules/@heroicons/react/solid/zoominicon.d.ts","./node_modules/@heroicons/react/solid/zoomouticon.d.ts","./node_modules/@heroicons/react/solid/index.d.ts","./node_modules/@emotion/utils/types/index.d.ts","./node_modules/@emotion/cache/types/index.d.ts","./node_modules/@emotion/serialize/types/index.d.ts","./node_modules/@emotion/react/types/jsx-namespace.d.ts","./node_modules/@emotion/react/types/helper.d.ts","./node_modules/@emotion/react/types/theming.d.ts","./node_modules/@emotion/react/types/index.d.ts","./node_modules/@emotion/styled/types/base.d.ts","./node_modules/@emotion/styled/types/index.d.ts","./components/connectwalletbutton.tsx","./components/dropdownbtn.tsx","./components/finalizevotesmodal.tsx","./components/gradienttext.tsx","./node_modules/twin.macro/types/index.d.ts","./components/link.tsx","./components/linkleft.tsx","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./components/navbar.tsx","./components/notification.tsx","./components/pagebodycontainer.tsx","./components/previousroutebtn.tsx","./components/progressbar.tsx","./components/proposalstatusbadge.tsx","./utils/formatting.tsx","./hooks/useproposalvotes.tsx","./components/voteresultsbar.tsx","./components/votecountdown.tsx","./components/proposaltimestatus.tsx","./components/proposalcard.tsx","./components/switch.tsx","./components/proposalfilter.tsx","./components/icons.jsx","./components/realmheader.tsx","./components/scrolltotop.tsx","./components/signoffproposalmodal.tsx","./node_modules/rc-slider/lib/interface.d.ts","./node_modules/rc-slider/lib/slider.d.ts","./node_modules/rc-slider/lib/range.d.ts","./node_modules/rc-slider/lib/handle.d.ts","./node_modules/rc-slider/lib/createsliderwithtooltip.d.ts","./node_modules/rc-motion/lib/interface.d.ts","./node_modules/rc-motion/lib/cssmotion.d.ts","./node_modules/rc-motion/lib/util/diff.d.ts","./node_modules/rc-motion/lib/cssmotionlist.d.ts","./node_modules/rc-motion/lib/index.d.ts","./node_modules/rc-trigger/lib/interface.d.ts","./node_modules/rc-trigger/lib/index.d.ts","./node_modules/rc-tooltip/lib/tooltip.d.ts","./node_modules/rc-slider/lib/common/slidertooltip.d.ts","./node_modules/rc-slider/lib/index.d.ts","./components/slider.tsx","./components/tokenbalancecard.tsx","./components/inputs/styles.tsx","./components/inputs/errorfield.tsx","./components/inputs/input.tsx","./components/votecommentmodal.tsx","./components/votepanel.tsx","./stores/usemembersliststore.tsx","./components/inputs/textarea.tsx","./pages/dao/[symbol]/proposal/components/votebyswitch.tsx","./components/members/addmember.tsx","./components/members/memberitem.tsx","./components/members/memberoverview.tsx","./components/members/usemembers.tsx","./components/members/membersitems.tsx","./components/members/memberscompactwrapper.tsx","./stores/usetreasuryaccountstore.tsx","./components/treasuryaccount/accountheader.tsx","./components/treasuryaccount/accountitem.tsx","./components/treasuryaccount/accountoverview.tsx","./components/treasuryaccount/accountsitems.tsx","./components/treasuryaccount/holdtokenstotalprice.tsx","./utils/debounce.tsx","./components/treasuryaccount/sendtokens.tsx","./components/treasuryaccount/deposittokens.tsx","./components/treasuryaccount/accountscompactwrapper.tsx","./utils/governancetools.tsx","./components/treasuryaccount/newaccountform.tsx","./components/chat/comment.tsx","./components/chat/discussionform.tsx","./components/chat/discussionpanel.tsx","./components/explorer/inspectorbutton.tsx","./components/inputs/select.tsx","./components/instructions/executeinstructionbutton.tsx","./components/instructions/flaginstructionerrorbutton.tsx","./components/instructions/instructioncard.tsx","./components/instructions/instructionpanel.tsx","./hooks/usehydratestore.tsx","./hooks/useinterval.tsx","./hooks/useipaddress.tsx","./utils/balance.tsx","./hooks/uselargestaccounts.tsx","./hooks/uselocalstoragestate.tsx","./hooks/usewallet.tsx","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next-themes/dist/index.d.ts","./pages/_app.tsx","./pages/index.tsx","./pages/dao/[symbol]/proposal/components/newproposalbtn.tsx","./pages/dao/[symbol]/index.tsx","./pages/dao/[symbol]/proposal/[pk].tsx","./pages/dao/[symbol]/proposal/components/governedaccountselect.tsx","./pages/dao/[symbol]/proposal/components/instructions/spltokentransfer.tsx","./pages/dao/[symbol]/proposal/components/dryruninstructionbtn.tsx","./pages/dao/[symbol]/proposal/components/instructioncontentcontainer.tsx","./pages/dao/[symbol]/proposal/components/instructions/programupgrade.tsx","./pages/dao/[symbol]/proposal/components/instructions/empty.tsx","./pages/dao/[symbol]/proposal/components/instructions/mint.tsx","./pages/dao/[symbol]/proposal/components/instructions/custombase64.tsx","./pages/dao/[symbol]/proposal/components/instructions/initializecontroller.tsx","./pages/dao/[symbol]/proposal/components/instructions/setredeemglobalsupplycap.tsx","./pages/dao/[symbol]/proposal/components/instructions/registermangodepository.tsx","./pages/dao/[symbol]/proposal/components/instructions/setmangodepositoriesredeemablesoftcap.tsx","./pages/dao/[symbol]/proposal/components/instructions/depositinsurancetomangodepository.tsx","./pages/dao/[symbol]/proposal/components/instructions/withdrawinsurancefrommangodepository.tsx","./pages/dao/[symbol]/proposal/new.tsx","./pages/dao/[symbol]/proposal/components/minimumapprovalthreshold.tsx","./pages/dao/[symbol]/proposal/components/instructions/execute.tsx","./pages/dao/[symbol]/treasury/new.tsx","./pages/realms/components/realmsdashboard.tsx","./pages/realms/index.tsx","./pages/realms/new.tsx","./test/pages/index.test.tsx","./utils/associated.tsx","./jest.config.js","./node_modules/next-transpile-modules/src/next-transpile-modules.d.ts","./next.config.js","./postcss.config.js","./tailwind.config.js","./test/__mocks__/filemock.js","./node_modules/@babel/types/lib/index.d.ts","./node_modules/@types/babel__generator/index.d.ts","./node_modules/@babel/parser/typings/babel-parser.d.ts","./node_modules/@types/babel__template/index.d.ts","./node_modules/@types/babel__traverse/index.d.ts","./node_modules/@types/babel__core/index.d.ts","./node_modules/@types/connect/index.d.ts","./node_modules/@types/ms/index.d.ts","./node_modules/@types/debug/index.d.ts","./node_modules/@types/range-parser/index.d.ts","./node_modules/@types/qs/index.d.ts","./node_modules/@types/express-serve-static-core/index.d.ts","./node_modules/@types/graceful-fs/index.d.ts","./node_modules/@types/unist/index.d.ts","./node_modules/@types/hast/index.d.ts","./node_modules/@types/istanbul-lib-coverage/index.d.ts","./node_modules/@types/istanbul-lib-report/index.d.ts","./node_modules/@types/istanbul-reports/index.d.ts","./node_modules/jest-diff/build/cleanupsemantic.d.ts","./node_modules/jest-diff/build/types.d.ts","./node_modules/jest-diff/build/difflines.d.ts","./node_modules/jest-diff/build/printdiffs.d.ts","./node_modules/jest-diff/build/index.d.ts","./node_modules/@types/jest/index.d.ts","./node_modules/@types/json-schema/index.d.ts","./node_modules/@types/json5/index.d.ts","./node_modules/@types/lodash/common/common.d.ts","./node_modules/@types/lodash/common/array.d.ts","./node_modules/@types/lodash/common/collection.d.ts","./node_modules/@types/lodash/common/date.d.ts","./node_modules/@types/lodash/common/function.d.ts","./node_modules/@types/lodash/common/lang.d.ts","./node_modules/@types/lodash/common/math.d.ts","./node_modules/@types/lodash/common/number.d.ts","./node_modules/@types/lodash/common/object.d.ts","./node_modules/@types/lodash/common/seq.d.ts","./node_modules/@types/lodash/common/string.d.ts","./node_modules/@types/lodash/common/util.d.ts","./node_modules/@types/lodash/index.d.ts","./node_modules/@types/mdast/index.d.ts","./node_modules/@types/mdurl/encode.d.ts","./node_modules/@types/mdurl/decode.d.ts","./node_modules/@types/mdurl/parse.d.ts","./node_modules/@types/mdurl/format.d.ts","./node_modules/@types/mdurl/index.d.ts","./node_modules/@types/normalize-package-data/index.d.ts","./node_modules/@types/parse-json/index.d.ts","./node_modules/@types/prettier/index.d.ts","./node_modules/@types/scheduler/index.d.ts","./node_modules/@types/stack-utils/index.d.ts","./node_modules/@types/ws/index.d.ts","./node_modules/@types/yargs-parser/index.d.ts","./node_modules/@types/yargs/index.d.ts","./node_modules/@project-serum/common/node_modules/@solana/spl-token/lib/index.d.ts","./node_modules/@project-serum/anchor/dist/coder/accounts.d.ts","./node_modules/@project-serum/anchor/dist/coder/common.d.ts","./node_modules/@project-serum/anchor/dist/coder/event.d.ts","./node_modules/@project-serum/anchor/dist/coder/index.d.ts","./node_modules/@project-serum/anchor/dist/coder/instruction.d.ts","./node_modules/@project-serum/anchor/dist/coder/state.d.ts","./node_modules/@project-serum/anchor/dist/coder/types.d.ts","./node_modules/@project-serum/anchor/dist/idl.d.ts","./node_modules/@project-serum/anchor/dist/index.d.ts","./node_modules/@project-serum/anchor/dist/program/common.d.ts","./node_modules/@project-serum/anchor/dist/program/context.d.ts","./node_modules/@project-serum/anchor/dist/program/event.d.ts","./node_modules/@project-serum/anchor/dist/program/index.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/account.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/index.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/instruction.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/rpc.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/simulate.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/state.d.ts","./node_modules/@project-serum/anchor/dist/program/namespace/transaction.d.ts","./node_modules/@project-serum/anchor/dist/provider.d.ts","./node_modules/@project-serum/anchor/dist/utils/bytes/base64.d.ts","./node_modules/@project-serum/anchor/dist/utils/bytes/bs58.d.ts","./node_modules/@project-serum/anchor/dist/utils/bytes/hex.d.ts","./node_modules/@project-serum/anchor/dist/utils/bytes/index.d.ts","./node_modules/@project-serum/anchor/dist/utils/bytes/utf8.d.ts","./node_modules/@project-serum/anchor/dist/utils/index.d.ts","./node_modules/@project-serum/anchor/dist/utils/pubkey.d.ts","./node_modules/@project-serum/anchor/dist/utils/rpc.d.ts","./node_modules/@project-serum/anchor/dist/utils/sha256.d.ts","./node_modules/@project-serum/anchor/dist/utils/token.d.ts","./node_modules/@project-serum/anchor/dist/workspace.d.ts","./node_modules/@project-serum/common/node_modules/@solana/spl-token/node_modules/buffer/index.d.ts"],"fileInfos":[{"version":"89f78430e422a0f06d13019d60d5a45b37ec2d28e67eb647f73b1b0d19a46b72","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06","e6b724280c694a9f588847f754198fb96c43d805f065c3a5b28bbc9594541c84","e21c071ca3e1b4a815d5f04a7475adcaeea5d64367e840dd0154096d705c3940","746d62152361558ea6d6115cf0da4dd10ede041d14882ede3568bce5dc4b4f1f","2cc028cd0bdb35b1b5eb723d84666a255933fffbea607f72cbd0c7c7b4bee144",{"version":"abba1071bfd89e55e88a054b0c851ea3e8a494c340d0f3fab19eb18f6afb0c9e","affectsGlobalScope":true},{"version":"927cb2b60048e1395b183bf74b2b80a75bdb1dbe384e1d9fac654313ea2fb136","affectsGlobalScope":true},{"version":"d8996609230d17e90484a2dd58f22668f9a05a3bfe00bfb1d6271171e54a31fb","affectsGlobalScope":true},{"version":"43fb1d932e4966a39a41b464a12a81899d9ae5f2c829063f5571b6b87e6d2f9c","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"4378fc8122ec9d1a685b01eb66c46f62aba6b239ca7228bb6483bcf8259ee493","affectsGlobalScope":true},{"version":"0d5f52b3174bee6edb81260ebcd792692c32c81fd55499d69531496f3f2b25e7","affectsGlobalScope":true},{"version":"810627a82ac06fb5166da5ada4159c4ec11978dfbb0805fe804c86406dab8357","affectsGlobalScope":true},{"version":"62d80405c46c3f4c527ee657ae9d43fda65a0bf582292429aea1e69144a522a6","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"75ec0bdd727d887f1b79ed6619412ea72ba3c81d92d0787ccb64bab18d261f14","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"12a310447c5d23c7d0d5ca2af606e3bd08afda69100166730ab92c62999ebb9d","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"9d57b2b5d15838ed094aa9ff1299eecef40b190722eb619bac4616657a05f951","affectsGlobalScope":true},{"version":"6c51b5dd26a2c31dbf37f00cfc32b2aa6a92e19c995aefb5b97a3a64f1ac99de","affectsGlobalScope":true},{"version":"6e7997ef61de3132e4d4b2250e75343f487903ddf5370e7ce33cf1b9db9a63ed","affectsGlobalScope":true},{"version":"2ad234885a4240522efccd77de6c7d99eecf9b4de0914adb9a35c0c22433f993","affectsGlobalScope":true},{"version":"1b3fe904465430e030c93239a348f05e1be80640d91f2f004c3512c2c2c89f34","affectsGlobalScope":true},{"version":"3787b83e297de7c315d55d4a7c546ae28e5f6c0a361b7a1dcec1f1f50a54ef11","affectsGlobalScope":true},{"version":"e7e8e1d368290e9295ef18ca23f405cf40d5456fa9f20db6373a61ca45f75f40","affectsGlobalScope":true},{"version":"faf0221ae0465363c842ce6aa8a0cbda5d9296940a8e26c86e04cc4081eea21e","affectsGlobalScope":true},{"version":"06393d13ea207a1bfe08ec8d7be562549c5e2da8983f2ee074e00002629d1871","affectsGlobalScope":true},{"version":"d071129cba6a5f2700be09c86c07ad2791ab67d4e5ed1eb301d6746c62745ea4","affectsGlobalScope":true},{"version":"6c55633c733c8378db65ac3da7a767c3cf2cf3057f0565a9124a16a3a2019e87","affectsGlobalScope":true},{"version":"fb4416144c1bf0323ccbc9afb0ab289c07312214e8820ad17d709498c865a3fe","affectsGlobalScope":true},{"version":"5b0ca94ec819d68d33da516306c15297acec88efeb0ae9e2b39f71dbd9685ef7","affectsGlobalScope":true},{"version":"e8c9f4e445a489991ca1a4232667de3ac36b07ba75ea335971fbeacf2d26fe67","affectsGlobalScope":true},{"version":"10bbdc1981b8d9310ee75bfac28ee0477bb2353e8529da8cff7cb26c409cb5e8","affectsGlobalScope":true},{"version":"583b4bd4e441916be4bf152ee323c4ffa6e178fd25df2ee2215ef05c7feb5ad4","affectsGlobalScope":true},"d20f08527645f62facb2d66c2b7bd31ea964b59c897d00bddb1efe8c13890b72","5726b5ce952dc5beaeb08d5f64236632501568a54a390363d2339ba1dc5393b1","674bedbfd2004e233e2a266a3d2286e524f0d58787a98522d834d6ccda1d215a","714637d594e1a38a075091fe464ca91c6abc0b154784b4287f6883200e28ccef",{"version":"23edba5f47d3409810c563fe8034ae2c59e718e1ef8570f4152ccdde1915a096","affectsGlobalScope":true},"0e9c55f894ca2d9cf63b5b0d43a8cec1772dd560233fd16275bc7a485eb82f83","d53b352a01645c470a0d8c31bf290ba791fc28ade0ce187a4a50f5c2f826f75e","5f0a09de75bd965c21dc6d73671ba88830272f9ed62897bb0aa9754b369b1eed","2b34e7fcba9e1f24e7f54ba5c8be5a8895b0b8b444ccf6548e04acdee0899317",{"version":"06d2be99c3dd2ff52114d02ee443ba486ab482423df1941d3c97d6a92e924d70","affectsGlobalScope":true},{"version":"bfd4f140c07091b5e8a963c89e6fa3f44b6cfcbc11471b465cf63e2d020ad0eb","affectsGlobalScope":true},"a106a0bea088b70879ac88ff606dc253c0cc474ea05ad3a282b8bfb1091ae576","c98ce957db9eebd75f53edda3f6893e05ab2d2283b5667b18e31bcdb6427ed10","37eed30fc8318b8ac76eac6f41d0758a9d0bffd8f3ff353e3ad0f8717dd08d92","9aff68f1b847b846d3d50a58c9f8f99389bedd0258d1b1c201f11b97ecfd36f8","1978992206803f5761e99e893d93b25abc818c5fe619674fdf2ae02b29f641ba","05fbe81f09fc455a2c343d2458d2b3c600c90b92b22926be765ee79326be9466","8e7d6dae9e19bbe47600dcfd4418db85b30ae7351474ea0aad5e628f9845d340","f20ea392f7f27feb7a90e5a24319a4e365b07bf83c39a547711fe7ff9df68657","32542c4660ecda892a333a533feedba31738ee538ef6a78eb73af647137bc3fc","0ecacea5047d1a7d350e7049dbd22f26435be5e8736a81a56afec5b3264db1ca","ffcb4ebde21f83370ed402583888b28651d2eb7f05bfec9482eb46d82adedd7f",{"version":"fcb95c45150c717706119f12f2a3639d51baa041cd5bb441eb8501e04b52c501","affectsGlobalScope":true},"a7b43c69f9602d198825e403ee34e5d64f83c48b391b2897e8c0e6f72bca35f8","f4a3fc4efc6944e7b7bd4ccfa45e0df68b6359808e6cf9d061f04fd964a7b2d3","73cad675aead7a2c05cf934e7e700c61d84b2037ac1d576c3f751199b25331da","8c3137ba3583ec18484429ec1c8eff89efdc42730542f157b38b102fdccc0c71","74594bec8a9fa1a2cd40268f9ab95d0c44b6080345da6e8b913939d19b1d57ae","94ca7beec4e274d32362b54e0133152f7b4be9487db7b005070c03880b6363aa","2d713cbcbd5bcc38d91546eaeea7bb1c8686dc4a2995a28556d957b1b9de11d9","bbf21f210782db4193359010a4710786add43e3b50aa42fc0d371f45b4e4d8d3","0b7733d83619ac4e3963e2a9f7c75dc1e9af6850cb2354c9554977813092c10a","3ce933f0c3955f67f67eb7d6b5c83c2c54a18472c1d6f2bb651e51dd40c84837","631e96db896d645f7132c488ad34a16d71fd2be9f44696f8c98289ee1c8cbfa9","2c77230d381cba81eb6f87cda2fbfff6c0427c6546c2e2590110effff37c58f7","da86ee9a2f09a4583db1d5e37815894967e1f694ad9f3c25e84e0e4d40411e14","141a943e5690105898a67537a470f70b56d0e183441b56051d929e902376b7b2","ddc086b1adac44e2fccf55422da1e90fa970e659d77f99712422a421564b4877","515ef1d99036ff0dafa5bf738e02222edea94e0d97a0aa0ff277ac5e96b57977",{"version":"2708349d5a11a5c2e5f3a0765259ebe7ee00cdcc8161cb9990cb4910328442a1","affectsGlobalScope":true},"780058f4a804c8bdcdd2f60e7af64b2bc57d149c1586ee3db732a84d659a50bf","ae68a04912ee5a0f589276f9ec60b095f8c40d48128a4575b3fdd7d93806931c","19d580a3b42ad5caeaee266ae958260e23f2df0549ee201c886c8bd7a4f01d4e","e61a21e9418f279bc480394a94d1581b2dee73747adcbdef999b6737e34d721b","9c4c395e927045b324877acdc4bfb95f128f36bc9f073266a2f0342495075a4f",{"version":"ecf78e637f710f340ec08d5d92b3f31b134a46a4fcf2e758690d8c46ce62cba6","affectsGlobalScope":true},"4ee363f83d7be2202f34fcd84c44da71bf3a9329fee8a05f976f75083a52ea94","a7e32dcb90bf0c1b7a1e4ac89b0f7747cbcba25e7beddc1ebf17be1e161842ad","f5a8b384f182b3851cec3596ccc96cb7464f8d3469f48c74bf2befb782a19de5",{"version":"5d708266116e778d6a4140fca2ac36f71d99b4c68bc3be63a45ba8bf5ade5348","affectsGlobalScope":true},"b514b82ee3ee4e33b40987d35ffeccfdf0cc050f9c4af29e04b50e54b5fb757c","714bc11c4ece2d28d6b70207fcefce4651c138cc77ce0b8147a6bab25c66b67a","72cd580decc682538544d8507f98c38c9201f83da228d2a0c170bcaa0937a829","08935635053203ba941035573962605b91376318dbce19e97c71354f2241e0e9","4f24a713856fef84913f851b9a6929570e2417c1c949634958db3cae98f54066","c74616881631c4278189bf62e1dbdd001d60b768a5dc2ba2682af316c05c9857","562e66c29f4c43b32c203ca892cd6d5b6fa8e70954d23b4d0e2db0eb3d6b698e","764f5b39a73fd6371e5a118ee037b685cec4ff2fc3579225eb57d0f82a38ab18","9ef16c926d9df3f0911bc679c9b4d33e464e7d08c6947cc593bac3ecfc716b99",{"version":"f1adba6d225db6b4ffa5172c9bc8a18990598da3dac3a33c86ba82cd509cb20e","affectsGlobalScope":true},"226afb272b10e93984d2f6bdccef184b0146d9cbbd448167e09292fef413a335","93865b0723d744eab9c00bfe7a8ccd962d1f6a2047e4c7eecd18482ef8b87e8a","72c88123ada80953914b43a0d9810bb0ce8e5b25cd8b7965bfb6842ffff74f05","2766dee26ea113e9b491b7842cb44df57c4d79b17057b42607e09fc174bd411d","2b4fdbc425984dc7a29c4fce80f656698363f3c9cde55fbe26e620bd3fe0da13",{"version":"7edacdb86affe13b999e4f8bb6a87ebcb9ea2fb9c8f8d159321dc113d973364f","affectsGlobalScope":true},"cc0a1cfd544b39afe9931bb9d93b40572bbd24d837eafc44888edc9c72b00d23","3d0f430227f44476d4c66b603d0b7d126422a2e8978965e156f0028c6bbec9e7","3ffe1dc2de9e55a28e0e17d8974b1f4e1b222911b84b6be6c4f0ee14723851ad","ddae6e637e160dceec077487bbd9176f4a8c60b46596a86fafd3bbd1882f6232","6c129db1dc74f8a65d99f01dd9f08fe3712817b770c6fb9ff1d7686288c8d3b3","e990bfb110c37fa629d8679ac8bba89d36a566d2c0984ab0190a70e4d54f5576","d2d90639077deb5644ec7cee738126779eff847692d95ebcd9f76d6ef2f08cec","d2bd714b146c0c6c27a0bc9f16c96522ef1c829923fad14b6a33e2580a012323","9ff3163c1203fb6197f0224e546eccabd46d7892c2b2d77ff6719f304c77ab90","a2495db5f46921bbf7ebf294e0683df709f2be4d9468aa3fe2137edb5a63a03e","5d7e0d8f75012b66c3d505af116568163ccd415a3b59ce51bc751d7797cc5f73","d57766da0e70a20dbff6d4f4a2ab77793302817e46f5d7ea0677cf9c9c9e0537","a8c3c3a27e8c7b93d5bec55beeb89f31c293c516e9139f4cc2eb60f5a06acd89","dd1b2492877b4d5b42fc2724d18d9805248efc5648be6ebea3c70b8bbff0a804","733975885b2e625092587a54edaec7bc023ec33dd22e03426542f308e04803b4","358e67db39c642ac453310968e5d2764e2f42a854e7ba8fb17a4347a40e94c25","6e5f5cee603d67ee1ba6120815497909b73399842254fc1e77a0d5cdc51d8c9c","19feb76bded29cc201568f945579b8fdf6f127362934f1ba0a77a9e7143e9b99","61c414ea61cce0b5320f203678d6a0c72befad0163bfa1004df9cedfdce73985","c26df0988d0476dd990fc069f96db0a51c63672b6abf6211823445575fd88008","73d959256b4a510edadfff37562affea8f50d477fa4900908d2dcfbd239e6d79",{"version":"68883138d1fbe09c3b8456411c9b7cbae9df330cd371b9d713252a63788a7d8f","affectsGlobalScope":true},{"version":"fd83ea3cad04f68eec8410c4a614b78899b9b19efc2327189950f52d2f54219c","affectsGlobalScope":true},{"version":"d0d8bf23d0f82c4d630a05c27b993bbdbc2c372186f51a61aac48bd2cb306ec1","affectsGlobalScope":true},"608f53422a1ee2658a484fad060307614f0c3359b1a3dd228c9c24540655abb2","9a2578d97903ebb95888128d68ce3c13f539f168332c4888b267f2c67d77964b","325cf4f6db7cb37bc0f0e3f2677781513297dad5d52ac672e5da3bb1ed2427ec","8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","3249dece06e3f39e8887543172351d2a52fc3c4d7101280f0ed199f1bb3a9ad2",{"version":"6b47c480f691540dc6e41d4b4e407269e2ada29ceb86bb683d52ff2110d7a5ec","signature":"1957791a0e008adc2be578ab7af84e18672d5b8a046117265c2c0fceca4f7af0"},{"version":"b97e8f70da41b30d1259e69808e956f1700f7ec06eea97cfa50f28ad7bb30e2a","signature":"e1b123075fa96b8b9348a5cda89bfb7e9ba433ee45e11fd4bbb5fe40a1a840b8"},{"version":"14ed84426e6be36081f3394952d3aa7001475eb51b49da19ad1379d7f0113970","signature":"f3d55ac698a3843f5e2e4a987f5599b96e222162f4df80d7f4de6874d1d334d0"},"bc6dd50ac2fc9a7ca6488811b116bd0ddd606338db0bb97852c8fcf757e2d7f5","e5e2d6577c8ac83e55c95b29a67466c21a01c9ce7da51909fff0182ef209b0e1",{"version":"7fb448c6fe70ed69bee007528167e72c6bddea444e2df6dd1abffcbe7136f6a8","signature":"3050aa453bcc5c9c88da8ae96bcd59274f6c3cee0e2c832b1099493d60de1aa3"},{"version":"229d76082578d2d54ccc5c185f6a504e2f17d2ea8b825a58db523627313c3723","signature":"556063ebb8b46a60d475aae55232c8143f5623caa3ea7d024085a87896806a5a"},"b80c780c52524beb13488942543972c8b0e54400e8b59cee0169f38d0fabb968","52b7e7aa14a72e4ec83c92d6a2b506f6f5514f4764bfdea8e87d90f1cd42788a","bea0b7ff48a6fff813bac29ef613339091e7c55fef263bd57ca963ec10c6b052","46da10512607b9042fa4dcf0d8c9149027aa7c5144e6fd8de209cef8fca3dd32","56b4d0000f46aa21f407d414ab01d9b64906af4beb0aa9f7e92d562aac30d4d7","cae6f5684a1fa2e8144e344d83b706b80e4b0515755efefa388bcbef4623571c",{"version":"a4ee4577834f2be8124069915fecdaef6a9d8c482578c7ee0dd2137035cd0636","signature":"fc65eaf13fa671045ebdc811a8f4120263965c0b23185aedd2af70f1fe81c6c3"},"1d7d9e42cb2cc36dfc7ce35755ce67d5ff0f4a2ef72d3019f4c461bfd22123da","f97edc4d3b4dd0b32766e9633086497565c37657fb591cc84a35864db4bc2fc5",{"version":"880226db55671d8d4971215a147dcff0534f6fe2243faf879b7f0abdcf745cee","signature":"55b1d233c8ca1267203a9f1b832445b6c2818a6db4c715581192d46c61c9df84"},"e12f73f15418cf9ccac96e2af6e7867060a458f9b8011286f14bb93ece989d1f","03da7e82f6dde8b92796ba5940d2837c54076c2bcc07e1e1712cc0154983f3e0","4df0891b133884cd9ed752d31c7d0ec0a09234e9ed5394abffd3c660761598db","b603b62d3dcd31ef757dc7339b4fa8acdbca318b0fb9ac485f9a1351955615f9","5982e54644e80c656b76228ce88a79e43e25af8867a3b5afceecad2d54e684b0","be90b24d2ee6f875ce3aaa482e7c41a54278856b03d04212681c4032df62baf9","78f5ff400b3cb37e7b90eef1ff311253ed31c8cb66505e9828fad099bffde021","467f923050c865b709cc9e1136347c50879ac4f1ba17d0a246eb6b2883bdcda6","71c67dc6987bdbd5599353f90009ff825dd7db0450ef9a0aee5bb0c574d18512","6f12403b5eca6ae7ca8e3efe3eeb9c683b06ce3e3844ccfd04098d83cd7e4957","282c535df88175d64d9df4550d2fd1176fd940c1c6822f1e7584003237f179d3","399a35805f9274c83878e64eb12edbd6ed943e36811cb474591bf170d045a0a3","11a9e38611ac3c77c74240c58b6bd64a0032128b29354e999650f1de1e034b1c","4ed103ca6fff9cb244f7c4b86d1eb28ce8069c32db720784329946731badb5bb","d738f282842970e058672663311c6875482ee36607c88b98ffb6604fba99cb2a","ec859cd8226aa623e41bbb47c249a55ee16dc1b8647359585244d57d3a5ed0c7","8891c6e959d253a66434ff5dc9ae46058fb3493e84b4ca39f710ef2d350656b1","c4463cf02535444dcbc3e67ecd29f1972490f74e49957d6fd4282a1013796ba6","0cb0a957ff02de0b25fd0f3f37130ca7f22d1e0dea256569c714c1f73c6791f8",{"version":"c0028a4b7110278e7940dacbee026257edb24b22a623dca3eb59cbfbdecd7f4f","signature":"fe6afc899fffc022dbdaa529ed5ec8e8a53bdfab4bc9dee95cf0ded0c353f867"},{"version":"69599c1483cdf5d35a577506bdebf914e77438a2af88c4bec9a977f0f6651a4e","signature":"2e1cd2deeed47b1c1168614361332f97929646496b68736c4a72ad8518609945"},"3249dece06e3f39e8887543172351d2a52fc3c4d7101280f0ed199f1bb3a9ad2","cbdea9cd8950e3e6a7ecb3bb3d8ceb5669c128693d19d73758f2fccb23c465ba","4967529644e391115ca5592184d4b63980569adf60ee685f968fd59ab1557188","3249dece06e3f39e8887543172351d2a52fc3c4d7101280f0ed199f1bb3a9ad2","3ba32d8f95afee3533bd255b90de8c7f7dfa85cd0c8963cc7513aca83e2cc2b1","2fff7b80f6a8f16bf3309bb4bac741426530235adc6550d9bec8873023022d06","3aa5714245cc32cab16b2cec25a851849c91569b9efc97021e81c339e9779d28","bfda33a9bd005e584189ee79034ed282163b0c90eeaee2aadfea0ce1c733b0fa","edb93374b4ea6f2222aa4e2f04e90793826fbcb4362c5224b3465afb2886d83c","d4388e807950354169d445d7f2734eee5029624ea474c5b0c30ed71408f68033","1da46c4d87aceed16e759be7a1f80318ea681b487b65227c8482f63cdd443add","86fc726327a074f88e870235de004d02091c64cf76a965040eb7b6f046e72a9a","75f85cda5ed6def82fc221cbf1f17611baed4f187a2b86c28a558e5a5cadba27","1709b56ae20f3171665b5869763035c6c5edac9aec038cf19108c623c915d128",{"version":"f031cccb0bded31d56ebc07214f819ed99d550c86e2c44e34fb50a5fb85c8bca","signature":"1e566ed266361a74e650646c92a0da5a81dffcd8f45b51a16e75624dbf412c3d"},{"version":"06d49a030aac03e90a3be6603b050b570f30f57a060db955a31310aa4c236436","signature":"29956a756c05911c74b8d093b1e1d5ec5960ca10c9d70e6abcfb5d698c3b8186"},{"version":"370e0d336c32096a9b7fcd8810d3a13624583e4226cdbbad0d6ad8ad64a332b5","signature":"5b84d996694c71c93533ebbb9290482865a9441512a00052e72482425bd4df52"},{"version":"4b7a1bb5c7a9fa4275c0130db41f0f66fb7bffc7bcb28178174a213a991f97fc","signature":"47bd4ede7277f4c11ce862d5fe8185780cb212158b23afcc67ab237ed8c563fd"},{"version":"6117583afc03b8469cc3bfc76eec89e15ab4d367df8ecdef21c873b5a0ff9ab1","signature":"c5fa32e3e3924a5b66af3bdc7a1b88df71ca8ea523b2976813d6c5a2c1bef380"},{"version":"aae1685dc0e66205bfc5a3b303cc1e9011c8d5a9ee1fce6fc36adeb17f334083","signature":"4eed13dba0dc81cc95ff2e814f8f7a5c6fdf398305be46c2b5e56f3a5294e4d2"},{"version":"07361c73121e4fc16bb32a89f7aa06e9f1713e369fa48791b184d539acacbed7","signature":"8c7991bf4d9d92e633b9c95a9fbba4468a3568156860f4f0802b538d2c9498f5"},{"version":"bb513d41884a322d6274e1f2fec8abb3971c1f99c1af6a8d8ed4afbc7c146fe9","signature":"7a927f72594765541996a3b283bdccafe1f317e7867db7c62b9b3609b0e25bcf"},{"version":"d95c9913e57cded9f638e398f59f8c9448ee8947d2a9f268049eb21946035ac8","signature":"c4f4a0dcd594eca2b0bb7f307268999471bfccedd83d84907c63c24c15caac86"},{"version":"591c5dc9e52bb2da7fa5889ab274f980fc63a8e699c74dbd396f0d1bc9463406","signature":"09692f31903292166d800fe92da7e0ca4ae80dfebc44716698f18f732bece3f2"},{"version":"2422b1d1f400064b239e07fb64d5ca7437d21540aa4ba2656cf9ea1444f6c8e5","signature":"d16ddfdfbeda6b9880e9fe8b607824e5d0c81a30f8b1733b09747b6f2d29af60"},{"version":"ab82c9ef856c8ac190908a4e64318d4b51811532069146e8eec727247f94a415","signature":"5858c4abde57b110fc16b812ad97517ed8058ad98998161fe4ca72db8853b0de"},{"version":"60c40081fb386f773ee3475520b5af676b3107d22a02042227a2265f04f016da","signature":"04ac71c874a742b3d6fd6dff3742d0f1ce8f45e1ffc538541d5065704710e2de"},{"version":"abd3eb2875520442a92ed7641d9e4895d2a981f2fa4cecc460b8bf1e2416c84f","signature":"a6553ccb4521d783568281226c8f69d33e93eb56f7d330c86ddd61fcde162292"},"3249dece06e3f39e8887543172351d2a52fc3c4d7101280f0ed199f1bb3a9ad2","974102905caa6608e7842d749bb2a4922f8e2267de305bc25580173974ee4cdb","a6baf46d64032c534d761d128f82c8e37cbf6df56f658a4b9c2fb96da531443d","ce423291d1a4701149f825d76baab5b009daeeef70c6ce7c31363e27dc4a75c8","0b55dae91ed721a0932f5fa0bd1b339a37bd39244c3102ba33c0b2c5dd2d67b5","9c01a5cd8ed8a68fd2a05b4775388bf5398942b08b10dfe90dec84784cc48cbb","2b5fe3253ecc77ba7f4211cc84600435dad654d9478dfbd37ebea6f8f0ae3d90","5c2d8d3d277ef9cb78ad47036727da97c51beec1e5b9cd6b9858fa6f77c73391","14c91dbd4bb7fd9a9bb5a651a61007cd2e29341989b3c4ef75b3bb3fd548be59","9eca7f7811e1b73181fde6ed0b87993c6105e0a239ea893731f139a8bfcf43ea","1f3e2970478dfe05b2976d14f892e34bef7305b3cb006c569b31756e9694fdae","9375a92ad6982b53ce82255d26f6cab3de3c7e13319732ac0380dbc80780123a","1e66e17f9db34fc6b1e122459a3a8d38d9d55a666f5bac728fbdfcf3b4b43001","534070d39bfe28fbced16d3fc93ef41d0b7a07c77684310bc466954214315578","847ccc20490110124a2252c5a705dfe1355921470d2bd6da9af1d4943e54346b","bb14bd7ec42a3b7a65421f529795ee2bbe308387ff274561e128f4797d2503c0","655bff5c020fac6765570f881c689a3b162202b9b8d13b2c35ec0c69b3ec3521","dae492af52685ae68e6f6f90ff2a42422fe8a8fd99acd9b40705a9470c4f7bfd","139db12276e11f90df65778f7513bc3f85957d74fd0d1bbeb19e4a1dace4f662","2884fcc91f5d42a3288a75a558b036923e3d926354fa0c3fa0251c2c3d4b54e8","cc486017c03c0eb004237288602477ef2c636eed71d7ef0911da01c856b52ad6","4cb9df3ea8035d85480096a5ff94dbbcf941efb38c5c1ea77f1dc164d4342728","1a33fe7fba89d4ede6eeba8239dd1288df1b00314aa537c461f9a84d69395f3a","291b47b629e4c15e96bacb8d1f707573e72fedbb0fcab43f729e5f9d73560c3c","2b4c473686e1d6fc043a9db5afa517f19998a9d09972601a3fc077bd502aa59e","dcb4c621cc9951c8938bf035551b2a4503d77eeb054b99731cc181c3e8d7f529","2e2da62d0c8291a079b03bffc4b7288c925aa878367ef8eaceb37eaadf0910db","6f3b6750d038e578b1cabf6598d5e7a0c7b5b235132e9c0d76f6f6bb7dc7bc14","a680c84205139381ccddf3f7f26bc1a27066b15db04d76a72dee15fbe1ea5cda","21f450637f704c7947724bfc0aaed4c27dea9c09649d853e60a16b04941b7464","4080dc1aad52acb3805160da8b7854c214907ce46bb101d9e22fcea7d02345a1","6bb1262991371115912868118a90069252f54baee42d28c53495155dd9b235c8","d294a43cadce8b25f16871a046884c215c7db8777d00405cc1efd3d58bffb503","56a4f1ab7e166244f1a3bb2ca59b448e853c549db5f157fc2de76dc4e918b524",{"version":"6fb153685c6cbeba2ce5155c0e9e648b38bf3b78b8e859d550c0b719cae1e248","signature":"09d7f8776c55909f03c579cb90407673207c22ba24998be4a386a44ea936ca41"},{"version":"35cddc840baa0a7f66abdfd1a4f21427cbc96085a6270c07c30a4ef0a68a3495","signature":"7f03f51d6aee409a15e7a95d3ca399374c148e3316e0281745f51f37892984e5"},{"version":"01b50b0814c40b47a13dc67f8950c1c646f94081af3b7c71036f9b23b0e1bf9e","signature":"b0b82085183e8075b183250a06d7a46cce5583f87fb5bd9e24387dbbdb8ed55e"},{"version":"338fe401147d224018a0df1b20976f3bc4fc1439c5d03d4295c538ae07a5d832","signature":"ae511bc8c6e7ae6a25dd5b1e0a410cbe0c76bdf56d1642701d82502973480224"},{"version":"ddb0530aee314c56b2ce615e3efad7932c3407e7c5c319cf8037f2c0b74c3fda","signature":"8a55a85564fe737f36f962dbb04c6aa2cfaf19ddb1866dea0fbf8fca5a44bd60"},"ca1e5a8fedb07427f7819f1946d46b1771890aa17837a73b14716bd7e7ebdbf0","816ff4dc1cf5d143551a526e890764532421a7b7f2de5b5589402ed1b4e81a91","3249dece06e3f39e8887543172351d2a52fc3c4d7101280f0ed199f1bb3a9ad2","0d80232f8e92e0e2a665c25140f1efb1b00e2db9b333b671f022b97f51d5b3f6","f2660f82aa48c3a390293d3cf4ef399ac7072d1820e8f6099b64c53c43cdc9df","3f6bcd4f5fe06d5b506377f8cb8a25de7478f1805fa547a57f28797c7990478f","6d18bda368eca05b7622a70b1219a46563be0e32a4c270bf5ae14ed40163980c","4d3862a3280aeb32c03c945cc441f4047e4ef928d4b295d842d115cefb82ca51","6195acd1eb973cdc31bb3bf5d07b7daa1109d69656f7fe16c51e4ea8a1a41d26","b7ef345e83601f07a3afefd56238d2910e3eee4518b345f3ce1dab2ace8cd84b","2e6b000b62e47ebd0c1592f72afc9fa9c58339950d51a1cca201b0a82573a980","22326678a4d4edfb2bb90d419d8c0ea3a3366f655cac9c08cb79326115456b18","173680298b05409aecf21d6533f111f25a70dd6dabaf40bffe592ed7d4e522ec","17772737c58ea77531f560a5cfa3cbb7fee82d1a2e75850bf4310154e432353c","a226b02519cd880218548632c481a71115fdd0630c45a5af94b090e8ff996fd5","609023afef3cf78fef0266bc8bfd16e841a54b9e71afe11c2e03fc78ba1f7711","bfd08ecc97214003152ed8bdc833b74a70d1a02a759738c13e9353491d32fa8f","0df7174efbf78459a2ba16adef3da17bfa676b1aa96bc844376937877426a826","ca1a105f0bc654bad1e5a49ab971e81b61e5619918c7dd8096ebc8e7bd6b0cae","aa31b02250bd1dc31b11d39acc82b257f67e9829ffab80822f183d997a5a3c17","5028ccc9fafc0f82737443fca154e5fbb561ab7083d04073389a20d71d52e246","67e6d9816a647dfdb6bb7df2583509741e749f1e7b98c7c0fb51bd702275f264","58da69a7a0fa1eb7abc7342baf611361444b67834d15e8a4ae168bbfe527d055","4e54c6eab7c8e05d25b9253f54355e05d914f13d58621b2b60749a959beaee76","0b46abdbcb2d9b67794068f7f16516052ea46cc78cb5e79da460c49e659cb1a8","6d0b45425e4ac8604cf85b9f5f6a773bf1f5a84182683efb59f789eee37f26d9","f822706e3514b1e36b57127b57ea0e7f1ce7c10e2e24842a3f2c5b4b8569c97b","d03eea1cca6edf45374e795278dd24e4851af135a6cdeb16124ebebc467b2b6d","ab35dc841f5607fdbe878507e93de5008f4e1f6fe381e6b4c9e980ad9d2d2e9d","f6ed2a22dcba5a21cf252b330e380887f26a4e0113f395533df5efb1eff08e9f","448e5eef354833c0c10677b92179cb2ce3990e491117ab98f96f036585d4b551","43cf26a1c05dce4c14c4552f70454cc69eb710fdd092b4518958120f3fd92463",{"version":"2f57a873460c3bd4f3fb8d03df949e5bc6b100eaa088bb47f289584aab22ca34","signature":"8bed43390c93e140aa822ac24e408150ddad2c9527b2a5dbf459fc0eb9b13580"},{"version":"1de66cd14f163dd0f4dc9f83153cb20fc4b7a0da37231be799d15fea3613e270","signature":"5c0174a7671aa331bd28fb842e95f1e9554488d74ecff06d26434255600bd9b6"},{"version":"ca80a174c903a2209de408dfc3d4a86e9dd8733ce2749104db3b1c8be158f99d","signature":"d1695840d57d3fad15d3b840c50051567ff6301cc7820e02b443529f11d0f92b"},{"version":"68d2d97ca713169e0e1bb85f5bbf5956d5f37c7e8602eab6756d072057b41708","signature":"1bc7d541a04424184729ee5fbf02fabc578179b6cf44b65682f9b2b624860a0c"},{"version":"15a76abe168113179a2b9461884470c2588be54cab5cc448ba855a213e11938c","signature":"9727e465b06eba8af835e7fbb49c963eceffd7dffd5a23404fe93c8e49ed1e88"},{"version":"24c945bf817a688846aba3a1be917a0354b4307268935a25acd808eb392cb115","signature":"17e010681e7161d1d342ea4474c700c04f9356917981c16c050c5246ffd70368"},"3154a026075044aa102298fe9e6a7a14aaa26a06270680c7478a1765af8ffb09",{"version":"fe9eec78bcb931ac8d4670e4f675ed78a4e10ae9b02e267be06a6662116ff5bc","signature":"dd139b7e7066d772354d5f0abc1d66ce4e037775f1abcb8aa20dac7a630ee331"},{"version":"1a29d889b36eb264975cf952fcdca10d9c5369f803c362905a37718f7a648a08","signature":"76ec0a6b55f8c84581f46204b92bec9254e5341a38e3f1eb2cf9b280694cc59e"},{"version":"64cb839765fd72849db8a0ea421a73910ef03eb86920bbd97fe5bdc176497035","signature":"8319c12749c5af14cd40235271cf0b5dda8d149ddbcfc24820147ca2fc322587"},{"version":"3bf1c09f3bb437d0f73445e346a36c5036b37ec0df7116e21cab5f732b4bc504","signature":"c0a614eb787c4f3cd08cdf582061e841419b0f7530962ad02f4f118e03d46a3d"},{"version":"b0fa8fedc114d437d5437e23b3c2b28f5881d47243ba5b01345d970c32cc3dd3","signature":"e730b726b4a1a90580e2e6e33aa7eab2c62260da64d53b63da9337feba751db8"},{"version":"5fc91e4c9d5fca9d1b2d9b99af547208db2716cfac1a7d23244f77020f36d441","signature":"d4365a82152c11da83acd31ee7165dc7b46c3b12d2d7f785304169a17b100034"},{"version":"b56d43966efb421c3cacab86041c2dcfb9e43494fb31661c5ddb9fa1167d2b53","signature":"ce7234238ff73b1bdca98395244fb1aee9725ee2776d8ba54bdc3972a10b5fb1"},{"version":"b7d75d4b1d9255c2b41ade14c8edc97600a3805e82e47b367e6a7f9aefc704f7","signature":"6f156e2dc5eacefaaa11af7ed299905b17cd31af817b034a5107e94cc923cf9e"},{"version":"36637bd48dba4c101bf48751ac7c27cb17a8748cd0255097e767bcce99e858bc","signature":"b7b6170fcdbd120a81152ba092efe28bd8c80b0062e3945f6686c007770eaa91"},{"version":"300ed0ed3134854b4eda394cb2206a1ca55366678f0a02f73534be5ac37b3063","signature":"3b1d66b6cf695e180bbd8310ccd38a546259654ba4f08308ba9beb7d81cc860e"},{"version":"0e8ee18c23a83ef51f0cdf7103b08e380ac1532dd88167ee33f2e9187276b5a7","signature":"3caa182849d0d7ca5c42a33fada1c1116e7a4f4ea12359c448e556e84950718d"},{"version":"ade4ef9bc2c5b7fd1fbca70820a00e2e95bf93b0c1e515151e1bc23bf0a1069e","signature":"b2dbd76d8b6ac0c02a438b139f339060aa4a639a1d1c4b95b1209119d62b993e"},{"version":"b118d91bdf5b4c05c6b2d40e70f5cbe4e3d32005d8e2a468b176f18b083a05cd","signature":"fbf0c83a5a5391461329d5643fdf2ad9c3e7ab3bafb4fc111f3c534205c606fb"},{"version":"60d6dd6daa6314ca5c296350c6d458bd8bda908f23a496a832e806768712eebb","signature":"00f62030acac69af81330b79425c70a9a5e28d74cb2e296e6ec6edae9ab39a29"},{"version":"75245c1598a6051baeff2cd0d29d651927ea57660541b48072b522ebbc70a42c","signature":"58f4ca6629289e0aff14f93799be66c4c4d634af1cb98c3a045455547cbbbe48"},{"version":"4f7c4093c04f1ec43d7b8167e115e8686f085f5536d849b49b742eb85ad59fe1","signature":"f4e92921c6a0101f7c9ec53788c51ef00ee53a6e8ecbba5faed9e8db5a4caa16"},{"version":"2fbe79a0dd780551dc8f93c2619a537bf8d53418a59a4a45e2b6d7877ca52725","signature":"5d4b66d26461d0014cbc86a8c939e0903ca4dfacb4def5d638374ac7260ae1dc"},{"version":"676fecee6e0f2974576f9750cbc8e6a8e8d8527d2306f4a86b93a508aae602b0","signature":"c6f64be24d5551cbd7f015fadcb3a59494168efd12341a4e7980be06cda8bc47"},{"version":"53dbe747852868d4191f98b6d4854422c34596e6ee81c35ed2691c9ea94f81e0","signature":"671bf54809540f54887f84d809968b2cf9dbc92f375e6211422cbc173dddc3d0"},{"version":"5fb68cc1f62cde377b950d05422d6c0da988b1e9185202ab60762787269f44ec","signature":"1b0b9eb11bfdcc3d8ed0efcf337ff9e2319b7fb89fad54d50e728adbe09d9eb4"},{"version":"73f5a268612d2c209192babf9c337c2aa72c2f4d73235d4b4bf45e5b924aca2c","signature":"3c55ad1f31f6a9fb1fe3f4edfa83e64b7c384271e2cc91b11adc7645db335e0e"},{"version":"be3b0bf1b8204d2698ec9da9d3a5152809883fb27620c6fc2ca9d6fb82166bfe","signature":"c70739a9b346d1e04591c92d3e9ed9aea0517f59cb6c9457104984d62426aafb"},{"version":"0ef4c5c98428735d2a853819173121f5005c70f878380bc3e05445c681f3ca97","signature":"79aaf94469f5be72c6b0e3ec39493cc848ed3b938bb39ff8b128adaff2aaec3c"},{"version":"1c239b2ac4ba1877a58ce46640cd05df59955abd65146be33dbc45ae9e199514","signature":"ee720382b3379ab9d0888036597da85e258c5b91a1e15ec6d4fe627e2e52077e"},{"version":"4f2015707d73ec934ed686d0376266a95a72b0cf9232311bd0220d58075c5b28","signature":"870ff3f2742bcb6178e5ee2180fa86066d59ab0fc47c66b6a34fce24a983a9a2"},{"version":"0ced94846ef9bee66265c59a7e452a58c72f941cee2f879d6a297debc2ec3850","signature":"f51cc208877663ff52749db8fad4d2842f23abc03ca3c6a486e7bb26982f6afd"},"db041f8d748028989cfb853a3e2e8df3a119080ce9388126d5fd2d6095326a7c","342d8efa6056584a87cc9de5f3f8e2c6f50c1b56e32e9d0eb109820b4d327b4b","bc8925a1d55b3fabc95672043efb4de9cf29b07db2f3fcbf711346da772a897c","342d8efa6056584a87cc9de5f3f8e2c6f50c1b56e32e9d0eb109820b4d327b4b",{"version":"58322c151ede820bf2b7754505901c8c2ea2772072438d28356736593b8ad293","signature":"6d2f8dde8884e3171d7081b5e7465a44103995c3c553ac3e6db9aad61723fa94"},{"version":"00294bc685bae59a7b5461403a4b49646524eaa46ce4624c2e7e795c57c870e2","signature":"f5da69783fd3e02686af573ae9afc72e9f624f1bc7e2292e0f295ad7bb522bef"},{"version":"4e502bcb076f2893cc41122b4ad730c2492bef1f1ac2b947252925691fd3d709","signature":"c116c4b65d5c488d08312e1ce84c1b1fbae956ccef95bbc47deecd79027e104e"},{"version":"255808caec502477d018c3b96db804fbbfdb7e90d5f911f88322000af090634a","signature":"a6a164c2c3f3a57f2e68ea57f7ff9fa8cbbf094293bfafc2a37350b4b3b20f57"},{"version":"35f459cedff0b21469eda5aef2da9c56d343d74238e217fa254388d8ae25769c","signature":"769390707d6a056ba7eaa1c759fd0b0759cde15f4fdd314371c66bac48a96d1f"},{"version":"9e05d37492bdc806c6134770b95ca773bc85139a6b3271e240edafe2696ab42c","signature":"4ded2e880e1ca5d27c1d84a7a486f3541eb081ea62509e7bb9b6e7a28bb53393"},{"version":"d7c52ef6db35f7df4bfe7b7bab0f8b8f52e667de14e6499f338ef97eb62a38f4","signature":"bd3321cd9b76e2d14d54ae0c231d12ffd3155905f38732513c667452c95f8206"},{"version":"fc1f6b763a3157304e1409cde65fc415f27bc5c822e2b8c540d4ab595c6b8fc3","signature":"43f406a0b08c3ec6fff8b950d51ee6cd85357ae8dcbde4761349fe5ef1f79216"},{"version":"3ece0319bb261935f1476a59cde5fca4af3a4edd05c3d5905b111c5337e583f3","signature":"cf09f2fde8d51b8fc62635894294962ff15bc093f29cebd06a385bc5fb41d0a9"},{"version":"4868e02be7bd8d95d63cfeea78d2f541b84f33180f977ba16c96d041787bc57e","signature":"17415a2f3fcb01bbe17d2fb9063a8c6f3e88a550db8114634392154fed621f39"},{"version":"7b3127c73e34a47a6e8645ae8f6560efc9a82ee7ac65ca677491f83a625f902e","signature":"22c75a37d930b24d63a4d0bf1b1e75c5a51ef7a9943d8d2ec5fd643b0f2acb33"},{"version":"d6daf73fa34a0029a5833048d294546b2f957e095bc55fbcb2050f30915f98d5","signature":"acce6c31d0b648e1be3326454a689db03bb21a2c6c63e5560f37434a72a0c339"},"2d7d2d542a30602ad869c89e497ffc1d45ebee0352f3806833bb1a73f88a26a7",{"version":"c592bf31cbb437c8fc1e9a9763c66578d6b281ca7fcc15d28da02014a9103b82","signature":"dbdd5b09211bc1ede6411a36e357a16af3002eddd9a005d20f3a61a362d07dcd"},{"version":"fd18ea34c9036330d6c8e58e476ba6d41f465a2c353052b36a7c41dfac5807f7","signature":"c1966ad07f6394da85b7b56f1cc9c7912cebb961da850815382abb414d4cdd83"},{"version":"09cfd85a74bd60162e5be5b9e19943d564e5afdd905289bf7bc350a3b5ea35b6","signature":"f3d384dcc5f922f2b8c7b4cc9ce75565bd0d824fdc6d80a9ba3aff91fa56d2c5"},{"version":"70a8103298df884321586a7d8938ca8acec7995bbbdc3aba501cb287975624a1","signature":"ab9a1076f3b3b3462831c3d07c1254a05cbdabaed7a6236936724d8cedeccb13"},{"version":"18cabecc5191ee6d1b9a70c6bc95de8ed17b7b0d54be541a9e8b86c6624fc585","signature":"716e377b31d23698f5b23ace0eec49b0948bf3e4f7156694ce55e4825efcf70c"},{"version":"d0c577f937e63975a5eb9a6df62ce9620e81bf1e0057888708538eac2e5bb858","signature":"4be8d508153ff97ea49d8b8b718d63bc4e62a9f165c3dbdefccc3d84ac8c6213"},{"version":"da0539eb9c41ab6854295f94b91646fb1959ef85b3c043791bdfc138b456f8c4","signature":"7bc751de013f1d4029320a59d9874e9adb87b4f38b11640bf1aca088beffab68"},{"version":"8aba8006c4ce44aa6f76d1e6c9e13a8659b1a8d5de2193419d280c3048e167fa","signature":"398786dc26a9686272f09d6233daf43c2fd8cf96eea3178f9535475d05293967"},{"version":"0a0b579a28a4f9d6ce843bae63d803606700ebf68402915aaa876b66b737cfac","signature":"758f3268b35c62ae837f686cae18d55abef5d58f946d33dac4c12ee3532b3200"},{"version":"686d54e5fe770390525b70ccbd001035c6d38bba67fca5dd2999b29387f41775","signature":"1be4b21381aa0daf77aa9f047769b9fbbe9efa7b2c2a8bf12ecd0a6b90125cdd"},{"version":"ebeae543e908652fe836a484d0328a12471a97b7cd63cfcfadc2302f95f7ff91","signature":"4ecf2e0f6268f884dc587ccd3101f49d4e72cbb726415f696d04c686d1232b6e"},{"version":"2ab52e693063a7097006707a5a11315f89ea1a21b6d0bfab986d66a64a8651a8","signature":"db5eab6c8f430f443d4abbc46f9790150c816127c1bc41ca50c07973b92ff14d"},{"version":"c5862f08e9c9f70a44f5c00eced2e45a94d70a02064ffbb1a7301123d9a7f31d","signature":"52ce89ff24eb0198226a37b4850f4e244b2e73325711f8ba14af3302178e4e1e"},{"version":"da403c58241af126251b8359985b2559c32d5c341ab7659650a1a46e9ab56766","signature":"d0fea815882353c56d0bdf07f03db0b9585da684abc2380239c41ebf209e9fe0"},{"version":"2db7c4d47b5eb775dd6ecba44742591fbb8c96057324343bf743ded7e78b7243","signature":"2508dce3fc7c6b38a5aaa20672456f73d87287e028bc56bf6a05b4a4623eece5"},{"version":"8c9146a883ca0e3f1aa03ed8a3b8fc7bdd9e768d8f323771d382678b14e19a8d","signature":"fd20964927dd2da52959d4a6d5745cdb44fbbc4c08690249334b5b7946cb38f6"},{"version":"99076745fe8e7394762d5742784b6c8cca35df73d2e6cd06679feda75d9d8474","signature":"933b60bc321255aa629a0d3f4fcf78eacca064dd595dabc1d0847b8f1a1ef066"},{"version":"5a481927ecac3d2bea3a4e97feabce099e813312d21f810ef0fc4bcb16e9a625","signature":"35724518efc0d29613dcc5c3c0a43c1c50e0519488bcd4ba220dabc361c597ac"},{"version":"284f693a38f4826e6ec6dea394d792834c84ae07815d8e9e4edc25e2a91582df","signature":"dce6f9d6fee882c6d2a5def10c03d5af08cfda5852cd88a386f51ac2863c158c"},{"version":"6d9f5f0eb9d6ef30a23df05a31c7cede4873c49ce5ee4d119405f0887e336d12","signature":"aaab3b408bcaaccb10a8fecc6252f3d528e15be94f9bcafc36868cd29b53a372"},{"version":"3a90fa8ec1fc0a196b760e484b65a4ca13b6c0519e38211f709e4a89f780225e","signature":"08893b098ebc6a973d1bc8a2f3a5788f16457cc780e306dd0b4d89a876b6dc67"},{"version":"3b28a2875dc3d92dc88e3c6e1a64ff17c06219a7778618a7899ff18fef814499","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"e51e34b4d8bd97569a7101c1b77234f6a1272cbb5bd50ed649b1da3f71070300","b562eed438decfd54e66977c32672e36ea5332118de4255fa8b078ea2aa60d94","d64e123a450152bd381a8371224f45c873a218accbc5032b96fa7efffac051bf","3cf63bba2868a5b90e9d0031c3501e3c3b50f66448e6ac48639b0d5bff9475f2","5a2194569d32a5cadd019b3ce2ade4d0e2c867b0fc2a48691940b7dca6ea8887","60c5577050b9e230486a60cf05ae4ae8ce9c7bd661061bacbb797a58cb561a66","5d72971a459517c44c1379dab9ed248e87a61ba0a1e0f25c9d67e1e640cd9a09","02d734976af36f4273d930bea88b3e62adf6b078cf120c1c63d49aa8d8427c5c","b1601667a5daa2b846d642276c318c8cec7b7235b3494e786c2a6a92809a058c","c116c5ff714c9c491b13616c1a8b2e69394dd9e6f28cead2d41b675860c0d99a","702a118d48a3dcef2324d30ac84185ab22717e118b0543dbe151972807b80ac2","ad2023038c6b42e4cbc195fdf874659e27f5c99191caf2572d2003fcd1dbf695","7f23902e7e648f8fff1daa2d28dc29c219d80920199fd0318257fb61308f88ba","e756ac94c7de6b07a7bd4a8cd365ac40ee289586c974e33f3676b3ef48d61698","cc7f5deaf4e1e2b8dde0645ae824d213cc71834a0d58d557f805f443451664a8","2ac36e828c9e778acbd5a34cd57c144a875443b45d6cee38cfebb9e5756e5dca","168994907c6427e885018396248c567b6214e0daa6b62d4fc1be334affa44c56","a47fcf458fa122a20e74794fb227c31edc1e2ab4595a21eccfef83f27376275f","60b3b7499b678b4ed4d33b566451ea66c25a0ec34f9b1b15abbba4291819c3b9","ee5c308961f2cd7a22d5e365736520dd92658dc424776aded00a7c589af4c71c","93d72cd6f97ba8fa778d340091a0b25a08ea336a2ac79cd7d33086906b5c498e",{"version":"1beb838d318c4aec78922af98f84e575f93f3a7bf706616a5aad4243c734c2a3","signature":"68aadb2b0f251940965c41c45b8d16627b2a22b37f0c9143cce5e76b04f1a3b6"},{"version":"050f75748d8e035f135ae5193b5298e685dd73d2cda617d43f13605f6006403e","signature":"bc623dfa17276c0070ebec1939990093c5533e8788151cbb955b0f4f127607ba"},{"version":"afd4e04029b5a734f9c2be5224eeb6d24d28357b619e70aed67cd777bf81d779","signature":"677f97a56b04ba9e4a078bbf4a234b70b43f3f909ddd78894a462660d07a2db5"},{"version":"dae7eadd549a0bdb790e260994fe17b30b3774d4d02e1ecc9b069da23855b811","signature":"94664cd5b3915aef784f2982bcef562bd516a455461c6b85c593a23d0007088d"},{"version":"430a3823f37255500b7e14031a1dee3c96ad7c7a4692c662acdb64943e7850b1","signature":"d2dd7c7e55fe3b06b14f696ffd66a0b7824278567cebf775ff5a0961c91e5b11"},{"version":"1084d6aae15e0911517a9a567c54ca3fdf0a550a744d4a51ad4d3ec601e10212","signature":"15d971e03444f1ce9c6f93504aa3fbf690590edc41a0311181e6a2ff87b457b8"},"a13146df0824561e09174a47c67639af36de365e50e4d2cc2d5a1edd775199af","0e8d6094fbaff1ee11d27c09d9b54e7623714069cddfb729b89531798e14683d","4d4b47eae7927f89a79ce46a2f072bf814f466419f02ee7de3e60046b7d41df4","d62360b8660f6b2c7de68e36b2b7233da02124e0a16fbb9ee9237068745db1d7","2179a0a6c7e3f02be0c970f9c8940933a98bd671501087c20273d107894e6338","3c52e1475454e7188e5e19862a29b96d9033a01a91e7020cf8a3b53adc1ab6f4","9d13883245a9371f3df7a771197f49e2ec77b30dc0e1ff63b17a54bb69da45a9","acc86849eed113cc7a2b70986484a2d6df210832a1e6bd74598b73eb3100e2ed",{"version":"3b1b1a903010dc8f91f85af3e5ee8088f8c6b756adf0c370c8c2facce88fbc66","signature":"7e3e357c418971adc242fe5df75a2a023863105374bcf25f73e13e6579879ac6"},{"version":"926d457c318fe1f98c2e7a9f3aea827909eff61ef1753e4455f4d77e2dfa3d80","signature":"f2d2fdf34df377959cb6981e04c9ededdcfb6be6ada8b5a9e72b6f6173a5e11b"},{"version":"0b9b779b0b34c34e9068c8b880ea4ca0af0d24b2730e7cb63d8334d6e875b6e3","signature":"ef6884b2ad92e06cf08e1460e2953314a0a9a183f4dc0e3e2ae8afdaca1dae25"},{"version":"6210ba10dda201b9abb774fa53afbc164c0ebbcf2ca93bcd28080f531e59ebf5","signature":"91a2495ffc8e95f2ad0f0fbc81caae20cfde29d53e750f9af7b7a9e6b9397ea7"},{"version":"333ac64028d0af469b7109f9569e4ede5442d3c1d02f961ca329b4e302dad53e","signature":"73a46da35e627a09476060b3ab104937be8634155a695557131a985f749b0b87"},{"version":"87e8cbce91f3413580635d24a427a7f3b74216d8c2710f8f9f3e2d1f64a60f44","signature":"fcfe9d94d76ae5e6bfe1d7c962b4d6e19bcc246a0d5835b950b4739ac25b8d74"},{"version":"6f8f3969797c702f759a5647d62f4d7eba3cc4f377eb541e5d1639b3a103dc1a","signature":"f5f4a4d31c79246876ab474047cda6c97a6ef36eae278d1f2970591633fe493f"},{"version":"f9c0cb6db684650f53be0e92340082505b05076b2659813a7608a109e7fe3077","signature":"1c5c8467dba99a7475da2f30578a696c61d6da65caa8351b07f2b951f94a17a7"},"51c7e7813dd1c1e7b5144ebff587c97bf8022bb2d6e12c6b3c4e3597686b8bd8","204f41659a02170064f953786aa397d89a0c095eb766f942902acb32780cabdc","193e45dd974dff7630cec4c07aa73963458dc9d137778a5890ae7532e1b71e48","a50599c08934a62f11657bdbe0dc929ab66da1b1f09974408fd9a33ec1bb8060","5a20e7d6c630b91be15e9b837853173829d00273197481dc8d3e94df61105a71","1657505fba9c49e14b9ae448370dcd08a671cb64b959359a8b310101538fb86c","b4000a0a525fa921e896cbdb32ae802c9684f0fd371b5fc69e7310f7918cc2c3","9df4662ca3dbc2522bc115833ee04faa1afbb4e249a85ef4a0a09c621346bd08",{"version":"76106f66a9c3d90d1571da9182464ecfe0ef3b541d0feded06505c675d165c07","signature":"dcd5e51ad98d191e4b9a22c19dec635b5cf4e050d39cbd2c46d69478036e393e"},{"version":"0d5a646bbfc9a7e8c69f16d653214a204d4518f7056c5c72ecac45d09173681d","signature":"bcfebc08913c3286311c57da67ff140552c65f53f713806104b731f8abf424de"},{"version":"b2cc95672d11e70e8237f52e4bbb33aaacd3b469088f6b7e171f238cb891cc67","signature":"03fce8fb9bb75267fa94765a72e75a8b21ceff4d8cbab7a2bdf8c03f66489659"},"08955a210c63ab8cd7e27028403a54760dcf9010b401946664627acc8d14cd4c","0baf3b364a794c5d00e97fdfa6da7298ce3533cbf4c7b0b3d831f85936d75aef","6c9bb2e6a6bbd1f2a97d23ffa80f9b583f6883a92e670e6f02ebe20234e2509a","0a93a88c97b04ae8f7e5260b18b369241ab292032d7622afa7a914cc07eba3ee","09b369e0621728733772ab1277891812cbfc71f0a0e520c21b55c3bdf4e94464","d0c3475089ca6e0e4db3b6f2f07e613c6ce44fe48dac112c8b4e13953a8b13ba","161e73719adcf55a378341b87611b146ff76b96c53c228ad0c9e48c876bcbf40","024d77dfe9faa588e031051d5cc667bdc77ff521f84ee0d39186140fa1704149","c95f7fc243bd30b30ce21fa0712a198f5d24c86d5fbdb43dc6563942141b67a0","b17a560a212278c7637d389caeddea89a382e03abfe62606be49a1baedaf8dfc","fabf8c56ae89c20931cdb1b73ce6f616d0278dd6cfc307989f1da0dfb8f55825","0d0e527597ed839aa40470e76d74986bc7efa845b0d518c1f16efc2bb05b11cc","a2d9d943eef68669e7718cd1de4f27dd25415265518dd5da5402666119a75a88","e3bd4f7fca64f279413d04eb09ac103b5f2881f6c57588d81413a217ffc9398b","446f332f928d0dce38a3fdbcadca1736bd0cd803cfd922c91c230d9f0015914c","a7f33b03586f5c94752d25c5d21487eb5561cd420bc983bc9f697bbdbef57509","08a80c8e6621da115605805b13ed0ffd38fb93f9ac4e38f10263c21deda53e2c","9f52a7a1692f4728eed184232c433e2d02873c395a45166c81b143c41a091000","4f827fe2ab5958e2196213d5f4503391ca2d57e4f3a4d5193821abb83eaf6f6f","57366c8ef1c5f0ece5bbd3378cce12f69f082c10433908b7fb467d3b2b923d6a","511401f94123d90d1b40b0e4bb43e4cc4fcf30ae639c77f77de89e56b51bde6b",{"version":"51974f49af96c9c0036f8afe375a932f3e28d157554732892e5daeb7611cead8","signature":"16678139fb916b662f6ff4b5e5dac986d49b3a973ad3d6f4f2811e76b30b240d"},{"version":"696a3d6706a7f5248dad2496f1261e75bedd48632b8a09902aacfa214a8e898b","signature":"1bb13c51f808fb60eb1d601e56975014f293d6442c5d11545cc2177f5e7dddc9"},{"version":"558e034d93b9a5cfb55a42f8217fb4f7f0a66796673dfc77455dc3e3671c75c1","signature":"c539f26e814503d191a1a83fe09c313ac01c8f435e31fc316300b2a88316001a"},{"version":"08ccccc97156ba30de324d0cf014b10774630ce44dcb64a0738a8e29deb2a8f1","signature":"30db28a970d6a049067d524161b31b75db3aea6e9148b53b16a5ccf42a49471f"},"3192b54876effeb978785858ec960ba7c57f81171070b5a0054a011ca09795f2","869ac759ae8f304536d609082732cb025a08dcc38237fe619caf3fcdd41dde6f","0ea900fe6565f9133e06bce92e3e9a4b5a69234e83d40b7df2e1752b8d2b5002","e5408f95ca9ac5997c0fea772d68b1bf390e16c2a8cad62858553409f2b12412","c512c0e8912b562194a3664dbe894a13b962b7fdc5e6467049e5776c9c573093","9260b03453970e98ce9b1ad851275acd9c7d213c26c7d86bae096e8e9db4e62b","c47202f28798de29986d7c1fcca4be4031fb72cd1c0dab001a8c9b19eddc7e07","969132719f0f5822e669f6da7bd58ea0eb47f7899c1db854f8f06379f753b365","8854881635098c8842b5e489272fc951546b2767e4f447a88ca9ce50a6ca9d34","2cbc88cf54c50e74ee5642c12217e6fd5415e1b35232d5666d53418bae210b3b","b96e8e3de3b3f500d50aefe067db5e2f3d8fdd51dc002727038c1b19c483205c","5ea98f44cc9de1fe05d037afe4813f3dcd3a8c5de43bdd7db24624a364fad8e6","3a1e3199054ae95161fc6a8418ee28cd774f1a258d1b0ee14aa71c48a1f8448c","0b3fc2d2d41ad187962c43cb38117d0aee0d3d515c8a6750aaea467da76b42aa","ed219f328224100dad91505388453a8c24a97367d1bc13dcec82c72ab13012b7","6847b17c96eb44634daa112849db0c9ade344fe23e6ced190b7eeb862beca9f4","d479a5128f27f63b58d57a61e062bd68fa43b684271449a73a4d3e3666a599a7","6f308b141358ac799edc3e83e887441852205dc1348310d30b62c69438b93ca0","b2e451d7958fb4e559df8470e78cbabd17bcebdf694c3ac05440b00ae685aadb","f0acaf12b028470af87ac5b89a60fc15ce0557fee633b80e32a5e5f0770cf8a2",{"version":"b2c0df25a73e3d587b010f16ec8ae03a5e13c2e49f246c11dd1f5ab86565572f","signature":"f8c7287695486afbda5ffe8f44cb415e1e9237d7d13f1bdaa2d847de24dd458b"},{"version":"28ee85a2b8f3a035d72d4158b6b217ed05b714ff67e9880b5e6688e07bdc0e95","signature":"7db10a1b57fea943de9de5d32d2effb36b860cb80fe54ce864b5b466fb7c5f5f"},{"version":"a255eb283713a51578c94cc849b39e629e4fa2e12b1aec15b2310f9c0411064d","signature":"282106d8089517ebf0c96fffc8932933a7a7ef64021e954e4c73bc622f1ce49d"},{"version":"3ee1b8ca041d8ee9da4b2d6160c8f60e396fc831a2c17bd19a5ec4f1ab201f88","signature":"818e21e2047991199a653d8e017ad820c2063aace734208585a169fe2ddec2a7"},"41a4b706f190423fb86f0fe568b685dc59c4a76760b506b02c10a3eb70ff880d","e26d25ac80157bf4dfaf1b15917520d7d5aa515389c7c0464911ab0129e58835","e305ba550c25a1807b5c432b31563f897c82111c8bce166e560029c94df204c3","a3e0936d6b795d2fc46850dc9c590152942ee297f3271dbd3d8588c3983592c6","2013af14579369e7822fb5f20f01268f3338141d4a382d5969144fe9c6a25ccb","6b7fcb23322518af97092aafb777aefa9196a1db215071fe8f96ae6f6101b498","a98d81cd9207ec9f1bd54d454809a60b267043dd56b165bdb6912bf9d63d4759","cd1aff7bdfa3af19aae3a21c958b932af6941ed89af069bd789f9ba5ad43377c","33e9caf10988726e5f7be53d2a4ebaad8db16e51b5f81e5ed4dbb0068f3a88fb","cc0d535d54cfec869fe4d28b9acda7e3427b25d8d84fdfe495021ad4cdee301e","f3e743e40c9a3b22de28624c8428e1df4cedc2fa3b3d99c546ebb95592ba1978","f8dab311b48db5e3b4361d76a629201098efdaa1b785120353f0ad221b263682","261b2daf69470a9e9d1ef26aa5e0c23f1611af380eb5e5f031c776ba57367741","e181f3ee5845726fa8077b39742a875d4fbaeb35f036765f95a77afcf982f989","9227c7d1182bc93c52d39d65472fef3f850a0d0145e1fac7388758d995878ccb","d90fecb86a618f308931b2efa45d7beeb73185db71ff184c1066b4e5f5900771","69af21e2252e417f1f26d2e6b1ded1f20dba11f066ce0690c53946f4369e00cf","fd4ae6100e700ea9e36ab622968f12ec03792f98f7da286cc44649395022453f","1adede2d971ad167525195ed811b8e0af2d17c728350689c470b6d5c5a3aa60c","4486b61dcb644ea4527e90b5bfbe7600b9ed50371d334a0c01e8fe661a99d952","dae0c9cc1c106f7b91a726813cfafe489dec4fb1e3f4f7c684d14e5f99f96b95","bf5fe5a9da7069bca0bb83fdc31ba9f624ac778c3ce25a06a3099d83a92a4858","7a0958a789740f9ddba6b7d80fbb1ae4d97da910970c1d59965ebc80304ad22c","54c9f10d962306a39448b924e73e631bba8221b5797ca4b2dcfb00dca78827b8","7125d9240eb556853fdaf7c892b60c630f6d01bd11580a32e9f299b0b91ca4f5","b339079a6ce4d7e86dc68ea8af379ee8700dfb205da5546ffdd3cdfe4be43df4","a97af3b565a5649c8cd983df40878da9a4bc080cf1a918199574f82a25fa727f","6e5996003f6555309d988ceae5b9952f3de47fc6e5110e5d64c612d4f37cb490","f07a1939b55120050d3fb95633ac6f8ebdb594e63eaf8f14c123480cc4bec03a","325e48e1f58a88830852e00f4fe7c515afef925a1254975a3ba9c54c9156f3fb","79caaa6f2db6cd3a1362f50b48ebae937a234b421c66516b8c98282b7d713d12","e7f0aa1f6c72a395ef5ac8749c03a31b00ea527abca2db5aea63804fef2dbb72","edca8fd9f3960acb4223f2a596d1f944320193afb076cb8b96afc19fb83144ec","511a250228acb3131a8c9eabaf45a53c3015c8a5abffc1c2e8b37942b779ef31","496927b9d1a6a7bea58453b9749b878ee1040ddc22de6db6e689db11ec111ebb","d17c142d0bbf9f20c696ed0224d1740fbbda0b0211225f2e48732809e15f2505","67e2a7d5facca25f1df14bc4fa4167337405fdc98b47df49b98aa2dcdaff5696","e16f1bf72fcbea703f1b9dba81e6faaf65e29d24128f9e09504c9a862f41c9d1","2e47b85f55b2c6606b137b4be24ea386c58bd9621370d2373e95b530c955102e","1f9fcdb7e24f1f9b391bb9200e32f6353781a1c604159a6257e9d8f1df7b9bee","3c788378c8c8d9525c168ce0578d536607896146b88bb5f6670ab19834be30b3","d16aba532f5391ccb1a61f90cf8e904ecabf3105641c398df0f4a79cac27d472","8d3a30c921fa2ea91d1bffc7fd9b8c42dc74bc819c503208122bd2e84cbffb5f","e4fb93fd2ec72ebaaa1e657435d607ed155da0005e6e30c440257d9fa877d834","ad103c0c76b809a8830ca4f9a8c8cb43b53d578bc63dc8c63b1342f1de90f8d8","7e15e2f23da806eabcf4bd0f1e48d7a5baeface210f05837171b50eed7d2b894","fb5e2afe7c4f988b615085166421f8d88464f6234abcc740ed861eccc0e17ba4","652af46b250ee5144ecb0eef7bcfa1647c88fd170cca974bdff9bb315f349f52","790daabde36636c46a264b1628f488ae49b2b378810383a1059ee722e82ceab8","89a58d0ab59eec78a6b6532e5d748f9942568891619633c890638a2912224ad5","9af24ffe92056dea7acff1dee779be364ad35e5f9861ca417d17bfb447a0a230","8fab0b106acb9de629cc3f7bf784187cd59d506d734917c4f140d02f0dcd167d","d0ba3b6aaef0c96be907938b6fb2a3a04a5db59de34a40f7e426bc7f10bb46d6","d91c919538e393ed3c649270a73f239ea7cd9f312dcee7dad037869a6eb0eee0","fcf5f4ff294643e6ea5100d09f40668a3a8744b73b8f1c397fac4b17ccecc72f","af3bebc2d30fe79abc9a505bc890d16af72f8ea21ec59009e9d57c2d8f6e0b01","784c657f85bebb1a7d94ef05e10f1cad4abdf32798203ef8631f7c3aca2390dd","e488fdf1efc9112b9ae08aaf2be027c3cc5603b916582c45d73bb3885728543f","a43b695758408470608b548841c97ad3827e453fe81ad835e29b9871129785f7","96dd9a7f52627f94b64da26c1ce05d2350941487861c8c27a0014c67273c8a40","0e754d4ed9a6cd6c131515ba94f3f1095fb10ac3cb0c20c2cbeef9e895f924c9","72cac4a4359c6a5e2e5c0ece767455797e46871350324dfe42ab14238f675729","cd091878f6b6994d9307156bda8a4419c7c41c524228d9e830f5fa618d70672e","bd3bbe444bc7cc28757c7669fb186a9ba326d4b65dbf99e18b2b5b9ca66edeb9","b10c73b3d7703d2d870d35631428cdec737d4bbc06706b6fcc6f6e058b8e1594","2543b46883befbefe10c2de9f3a0e7809de7baa09e192edda748443fd15d38d3","01cdf83ab596024078a6ce08ff990770326ceaf16f9081a8e369b9bc5110cadd","bbb9a17f2654caa1d34f49428c0e48ca0fd0d9550f5f82da6f544c924afa17b3","a9d8ba2c15cd99f51aa291034a1afff2f67f1f88259d2162eddaa25e3644032a","affa88c9484982a9aa35b30480059dadfe98c3bbd92f76513ae2d1d7e68096d2","3436c4b40e71c333e253578f6f3176870d4963d5f4ec14862ba5e40794bef8bc","a02f786598d002e2e42854d9bb4fc5a4ac03589538055a0eca03c9ec7ad35457","348863ce75f819f43557d134e5e7ff11a8ae582ac879349cdf9156bb696012f7","235ede03bdeeb87ca21b68fb1398ebc4749a924e2a7219977b71bfc9c51574a7","ec83a163716e8a8c2324e3f6b64c907ba7e5247b43df47f52edef954232c0211","15a76d1390ae38fe474023a51778887a6e39cc4204f65519a448ed7d1931275f","d7a9a81362adcd395f9db48531a89df84461595d189a234796e85ef983399042","7ac3584d37571a5ba61326f50860d843072ea95673e53d23b0b684635db9db00","96354248b7b7fe792c9545b7b153ef20763677692e9baa9aa6d1afbb17376ba7","5dce9f1eea7d40ad9f10295dff47b7de6fbb24b33858c0ff91aa75043e9899d3","d324bb068a3a98f3d7ff92eed388935a5ed4bd46b7678c5bf057b5a2ee9416d3","fe6e76ab5933ca777c6ce422c7023d44799d4832a8c5ce35e3592c8430867329","cc5beca247a7da7286a82c0f3b84686a922d0a402ead5d11b9ddd0dcdef5c762","24661a3d44d16268a8ac8260f35651526c54496ed5f29e559c066b7b7e6776c9","7c7ddd5cfbbab70612c216ab1d1f982468118fead1d57948ed31b41cd692c2fb","461ffe558e162cb3e451654eed59d0090a267818fde655088616be907007d654","829df07ac748fd372b8bcace5b46e6ce0420d2fd65c23f64b86e8a099b69a21b","162ff76724612621b2623b71139fae21981b276595c5e93a909b464c6eaf6310","4ec0abadee52b5287cba7929ce1c34e81a67a046c0299908158497ee85ff20b7","3b60b6d30d8be5b573e75d148abd155fb74cbce0795055965ad505afa4b181f9","5ea0f9746d216da9d45885462fdad43ed26dc4473ac6d289a94661c9a1e7bbbf","96198fa503333a1856039319ad4bc45c6e32afe2ede6a050e23fee2127139a40","e9dbaa3c845b96ebd98a7a3c59296fad4f5cbe4c2e471d0c54a248dab6d575f0","63c5fe7a04273a69125008737aa3c18212a1276b3a2f3892080c346cb589a716","adca096d8a06e8fe1f6f8a1d95dd176e0ec2216f5dce683c9c3656a9bb1e1f10","fe5cfccf2b757c44e7251d6ac822f4892d63f0dbbab920da4f24b893e655e836","e2a5e2a231048f1b0a8c6c123d524adeb3eda1464ac2413fd039cf5afa57bc54","8701130ab14da66b4e908e13c3ece584b420399cc543bafca971c414059ee5e8","811e9e98ddaacadbbcc92015fafd5f5ce0dbebc14f3536cbe225094b1d61f885","f7c8e5f19d7159bde8f8e9c6561c6e517953457faa86018a7f963b72863380fe","c0bc4b28c78bccbb158fb2e8b3e37a86fee5f26b6098a857befd864790da7cd8","e7b604762369c8fa5ffafb6e238a2c7af296e5a25bfa25cb33191b525f064cd0","dcabfb44bd25183c919819f87428fc589b20c3b9586825ec456f94cdb67bd316","41c39405eb8d94777d8b30d2bb295c258391ac4a45deef8d2f569b29bb82938c","f5786f9b0a39c790d245b33436e75576990e41e995e1fff1b0919833a57f4357","970025f12906d26ea3c1c381199eb6702b9c8cb0bec44edc02e86e004cf95eb1","8dafc03ad3ee7acabdb9254c702b80755bdafa7d7548cc6ffc21814e83055abd","e0dba6e973edfd7a5d8a7307ad1e6ec014b51fb7dac507ae132d1f9429016252","cc2f61e4781ad29f2aa93d4850de1b1d8313f242631f10ca17cc99411eb63022","6679676c1dc90d9c371f3de8430bf070ed36d2677d9ce3d2a336b54c5c40c2d7","cf2b168364792895c95f8f98f8fb662f07787e518e6d25b0f7c4aca9927a1bb5","db115e097d9ccc281414e7cedebfb0435d5bb14022f147331fb1bdad09404885","aa78c2b93bce87b73fccd6726cac3cac4f62927460ff3495f2a05553b4c04d3e","298104d50f65103c256ad79ff5128141000e4544a6afaa998d3099bee3975b84","a99f5ced5c95d7603c94c66a4619dfd0e737351bf20757de516b7f8ca193cce9","341b20f291eecbfefa6760a69f7f3f18b2094edcc794f4e78e903a5f0dd86fa6","238dd354909fc4a682e0cc4bd0d1eee8ba03197a4efa3cb284e502355eaec8de","8a3156a33e38b19f00d543a9f7a96054a0a4b051533449fb04267b4e533f55ef","bfd14082a4db87c2847135aab3d617ad7b488b3e65ac82f1620742548ed630f3","e2aa5b5cbc067b485de95616efb852886f4a1a43685ed7ea0ee8e08fec961cb2","3d6d27c275808a7e8540b1778a5d3808542518acda03f5c1ea4c9c5831058ea0","f534c1a02cd756679611ca2b36431b51715a0c59a070d413e292dfa23b9b5c6d","e26cccd0ef5654714877908c1674bee29a8e53d60c8c2d82bdffc12cac6b0fb5","ccf581fb8928f37fbd6509a7d8fa0d32156fc4eb414f434bf81cf7bd6849f7b8","69b227120a5245cddb0805eea82a0bea405872bcf595d2fce9fc03dd16133291","d6f495bfd0020102c67a6f80e411b00b913a001c468001b7cbe8c592c748f301","eb4463ba66be74eb04aeba3bded1f485a6ee90bed8c28a2c2573f0c983834790","a76187885d25a8aed20f71760c116bfb89ed1612d125bc190ab25a1a7a87ed91","76d36099aa1a0c7ea690ee1675ac4dd86cc62e2643cd40c097899268f8d2f7a8","815d7ee4cb5383f94b88688b8f2d70ce3e5df4de147d3669d225f8bfcffad673","afc6a3b94e405b3ae5d5038fe66f3b2412300c93ba1250805ee1a7ad19964ff6","b168bf198df3af94f54863a77ca14dcdb67af689d4cd876328f7c70bbb7b985f","54a40fe6e389146cb444299ef2d7d6e4ec83b05a9df2e7611fd1d1d862b2743a","65ec140c7cde7edee0f611bc84ce505cfa71916571e76f5cc197ba1dcde32f2f","969a96cb343d30bd8a28bad66c77049aeb0fabd9f608ad82caee751082685eeb","b30e161d3bbbe3f8d15b0bc5d9b47d1935ffffc7ced1099fcd84536c906af711","3eaea558e36977f924d85b3207406594568053d7a52950081b7603d112edefce","4918bfaa32bc0f63380d84b19bf5adff3188797b17b055417bbfdb09bd531d1d","5750305060905aff115cc0a6f295357099856fe72d76a1193204c23b4b9417f9","98195f663b1c09572bf794bf2fd7351b05d5895ed471589c0c79ae2e7c7d6b97","355e8226f1a83a02c2c5dc22781defbcf171df314f6f3205318851fd4550132f","8abeb772b7a7763686fd699980b74d9895863a758f2a6b824edb3d5e5c235078","dc1e2e53c9935f62739ca37d9acd484e83fd25bc051e5a330c9be0acbe774253","71a542cb540a3c0d4d954d311937a4df56157b0797489ea5cb8a9e57f449aefe","49fbd0ae0b53936499ca6293450230273cf299e017ac4a1cc8de936d50d8e696","d1a6aec1239a47bbcbbc55324d6ed293f79d4554f6bb0e411e206a9e22c50aa6","4e81f6b7048f1022bd8dd7dc18e43b4aca8967c4ffbfc8fe80bd4277936e5be3","5ff6328b404fb34d2828b501bd16f75bf17590d2b03a66a469a0359da07a06fa","149fbdfda86091cc37779a6eb3f01ac5c73d6e6d34a71e76bb3702a1ebdd8bf6","3c1e92d9a7a0c81d02f016c47de63a39706bb0e36231f2e6727a08cbbef6cfa2","ebec459a7d4933732cb453254997b9b9e7d17319dd40a29946f985719e297927","394dec85f81a33891c71f4e6a1b9a40afdbe93210d5dec749a2791deb57df5e4","19d2786de07b0dd973e5515d84182d2734f1c1ecf602929f75b30081fb20fcce","728d3db3ffdbd649c96fd64cb5766993cc8cb10b4ef207403fb98304eba04f57","8e4ae5371abcf89ca3059e621b666f1a340db0575f0c8635431417528f2d6367","de4f5917a7bf2c62cd3ab4c171620fd6e88a4a92902f02c0808ae67793e6baad","8f09f0abdd346cdbb1e545a44c85dcefeb047d07080628562a328d3e88160beb","4557c1259460570a893f9adb1547b5bdd19948497740c53fbaf654b20ded5855","ddf8a6692f74ae9ebdece687ec1cd9ff63811c5af27381b40c7996053a1b0504","de735626154dab7ceb24b208728b7461aaa5ad8848152ab0b39192e7bc0aa4be","c6296acd69aca14033c815aebc1b4c7fe72b92e44145dfe6432fe855c8c7b463","fe7df3fef3d25c0455e7cd4f36fdff7ad7b4d2163e7285a74d6a98a6cb48a282","7e9bf7c76b60c4402bd48996bfb0d1fa552a576991f9f73dbb856d15b0346793","29199bf01375b374d516e5c8d5d8ce1dac3c07cc52b00eebc0a7ba7b05baeb1c","7696d51d4ce2f2f21e9f73214396bfd823bee6c57f65d39e6a2264c40dd021f2","4b74f4072dacae8ba4f21abf5d042e5da499d027b0aac8e2d8f42f5f591453a8","d3b884fb07719c9457497fa9d5146f7977fed29df303347ff4175f630b903fcf","665320db7cce83346ce47ab3baa657b3115d796d28d8f778a98e7f23962b1247","3f471b5d1f378519b8276a869cb746d5cfc9b2bf4d7e5cbd0bdec3f9b5f81fe7","4afef388856e350489141ad7d90ab0767a02954d53d953f558237e04c89b5d0c","7b16f7f12771f8e25117321d1cb607e06be688ed8db002fe2cb13b716d0662b7","9412339ec64000a6dce5898fd848f025d31ec3b446872070cc041ade876c0c1f","726bc5d2505bf6051e80ede05a576e21d65118c077a8407ef4f9eca8d5445464","05f2af853ef135671b9482077d19a2935e3d924debbcf2f4803110bde114f113","134078b75b0105535f6164680bd73f88f9ddaa84b7e0126aeddd2af7ea27bcb0","264f0b10beaa4141a6bf228d2e22b19ff7baa76b39388da319bffcd1ced741e5","98682512d496bb618315de173d7a25ec363676da2457939f42b75a23c5acab87","1471ca4439a9dc9787976d1c9ca2b913908f4029465c87372d2efe2069c375f0","54b8abfc4713160ce97f91758ee1e8a547ba67c7328177d5e4c40613a3e87f79","60894b9993d7e1a7be37933c9bfe96b228ebc206cada93a44bca9d30e12d8d8f","236ec9d640fd6438a08dd2be3fb739352f147de529b4aa1953e4d4f74d6638b8","e75ea841bf22a156a7ae8f95eb7b1d4479da8c291019148d0a643027356b76c4","4111a7444998538da1f6f76378412547281e30cc5a7249b32e7402f66f83a492","4b9a4b3456012104409fde7f7631be98068daaafe1c49b627b8d92f033960b67","7f7840713032b2ad3bbc379ee2d401489b4e563294f7c87dbaf2424a6682beb2","b52b80732805d494ffee704b80f689772e1db9440c1728b907f7b25b3328d5ea","a805a58a4c72c7d513295fa7102284dd9cf76c1470e1845a6d7e9afa4bafa609","724e0ca25a06f553306e33a45951c368346cf1fc4b26b8bf4bf88b1479131659","b0880e598b7256855af6b9ea2aebdf47372444114264b56da9d25ea5f95d064e","3ff6607fc3c3a85814ec3d6e05e358d99773ee7c7b5e9deed5e086e39f5372a6","62247290540b91ed85258d7e8c67c7786e38bc1111429da9fa42b1b34e4ffdd2","656ee3f8184b5dfb87200f73350e57827dba056130d15064406487c0993f0e6b","b14c19907984b69ce25e011e6391ff6a150bb31f344d643f2a3d5af9aeb4ba73","2b77a0e88653109c708203a50fa23bb50406a9c8c7f61883c92e009f778387d6","2aa50966f709107108e7ad733c129c81f9e42731492948a9c23d4f8a0de5ae1e","080afc7aa193ecf03c78afe2e3d81cc3b18fa482f1bd955b4dca67bcbf220eb1","7282df0d72afd1c283644f5827159ffe0c899850fe121811e6e7e3eb77416868","07c70d4602003ffd9f23f8f6fc2693b7cf024a323a6d6146b11659105ae588fa","79eb7464a0215c82cf6fdc55b2378dc0a3aed416f03dc647fb6956975d446ec1","8cd341d72d1ce25d33dbe1681a9a5f27fecfcf65d426a0d0bb80ce97a1e37d50","4f750b488d0d1019bf8b6651e68689debd6106312ca7f4fca22627fc2d0acc04","4307994ad4d3a8d842a7d7da76f45f84e5eeaf1580e9b6071dd5fa6b8b21de19","87b54711b1c9791dd95b4ff88f814b489089f5b128f29e8a5fb7f6b8123f739e","4763437e8a65ec15310aa20a4ec288eae3de1b94b9426336ac423fddd482d70f","e2398ace0c73a5da036dbd6cab98008a251c709c56f1b665b6202e99ae3450dd","1a59a1ed95ac47cb6d1798a4dee9088b847f41491e57545e788ede35ed1204e5","c89ec75e2ebb2f5c2dce2d5f85ab59951cdd217748a49a6e0bd102fe69f5eb75","e653e5173f22f243892807fcd85800dfa4efe84be40e0ed1cccd15d62e1c9da4","973c290e94835130e51e934d078ab80e975a7bdf5b9563f5fa8de080a06c588c","5abc40134600b35d15fcc7305d5bc9e29942c64dbec6413137b55e77b689da1d","85c3303460c6339a77ec37ed9b7e06974f6d8e1351181b3f6f0c5180e0e7a76f","88d761b9b53ee5aa4ffe94d18e1c05c31ba8778beddcb8418f303c184f4f61f4","8ad1059fc2cf09ab5208fdc50a974a1a0c0f3737d81c2aa0718c583716b49f7b","ded7cab1c0c297958efedb1c4569674117693e0ebb6e8a1366cf7a629ba490c6","997c088bb8c6e1d869a689a3db0157d3efea26fa3fe49ece5f01044321949769","aa4d9968e228a15f4f93ba782c05f19de5aab2598ef8acd819ce72d6f4c9953e","1c4420d12393decf20734fff3f09f44daea5433869633ed11e9fed9b316523ec","25e74c23ca9d123ad4726803694ca249b958270fa3e98eb3de7f339f15241a45","87537c5c41597d3355cd28531f715bcfa77b73f0622d49b28b6064b3c0ba0ab3","56acbddbfb96d3e9502c73d87f216fd16b9954ce7fc345adaa51a054bbd548cd","ea5f8a7e470eec67d9b3a57a311393d9b8146d59e7d3965fc2a07745aabb50d1","d75a15490d5dc9b8bdd209200429a4cd31c139b1503e22e1ef743e6d4fd160f2","fb238a904625a2a0942f8f0aad2c96d5ba7b684b59890599a10d73c1bfd3f771","35c91fef4484772dedb9c253a07ad91912a8e349838bef1e7c85b93b6acd39c8","b92d1f42b729031910871b073bbf05b8d68994a91dc43a7fc64f88abff16db54","beaeb7cae58c1b074e1e5c4c0cc4e205b1763f0e9f413d7062951e1cf539b450","2059b7deab3beb764a2368200ead025358b48fe470bd6850500785d94c8fb5cf","da08b48bf74446b6f988e95f501693844d59d445487d89d2364b8bf431c8a21e","17c4db14970964450f5bdcd1349e6f3035419db7f7f9f88ee966b3b34cbaa8b3","a9e6f34151c8629364892059c1689e2f99775b3642a24854d0330112d6892cff","bf5fb8cea51021d395c45a092c1e97534d498c91812b99fdb07c658cf5990585",{"version":"c0884b5257b0e18721c353cd723e4867f2b64d0effc5af4f52c6714a30ee4738","signature":"f5823321df5c81cb8e165a6c2a60bfc9212a12cd44f2b4eaaba8a868721436a0"},{"version":"c615c88d81af3ba04ee8ef37cdc41fb05b320e0f23819ce9abc0d180a4af884f","signature":"42fcd0002b9c82a09505888f60b748c54d9f07dcc4d3b73eb020759f70ef9d55"},{"version":"82c2d46b810cd51467e62a4f74d93e80726e79b5b3c781cc1fc3b67e7d1e9198","signature":"41250c43b33ba8c7a8801bb58917d4858cd21311d92c7a46247d0ddf2cdd828e"},"a924f64903a0515c07cf5f91863acc874fd5d0b32023a8516b009e76b78171ce","ff5b102cf991baa9e21be3cce64aebfb510fe9b3471cd567037af9dc58a8f248","4fcc356d0447930d7303a1e8d0da7ef97a53d380cd8170cc438ffba0f24f7bdc","f10667e81d2aac535c8afdc137fd7c05719376da211a1f1a6dabe693138fb282","833363b2d840ee09e6d8772850cb712a686470ed897e316ff9b47bbd82b2d047","7dfba9572ac89d7d734b8dfd50d30866f3eb50e6f9a2067f11aad3c4214888b8","53e48b2ec7b468b93a8dc467e51d01d841b24baa8a49236ff4dcde08d98de8ef","f4aad1575cae557fa6d2851d504782531d0ad22c09604ca3f1b9531783406943","26dbd763026dc42722ec5754dba41c7d97abd6b930c4d1f3619a25702a215f20","cfe4e2eb40376ccd2351262594d1721c1d9bc26730af3e61a229b43c99a87e4c","0a6be9f2955b63ba2163362a78c4af5a284e9142c73c86ca744c3a7028d35afb","c9c5bab53f5aa5ba3cd099708bdb6f2f41749a5723b8a1e22e5ed4ca11caa02a","6df5536ac89c7eea9167a8fdf89957215c41a53fd8797f8dbd06ea8b7e384704","8a670db838f69bc208045f3299891e788525d9dc5e60674522173138b90a57c5","c39caf7f94ce482759519c9de1e1983139171291b8821c0960bfa8e1f8c73fa5","41a4b706f190423fb86f0fe568b685dc59c4a76760b506b02c10a3eb70ff880d","e26d25ac80157bf4dfaf1b15917520d7d5aa515389c7c0464911ab0129e58835","e305ba550c25a1807b5c432b31563f897c82111c8bce166e560029c94df204c3","a3e0936d6b795d2fc46850dc9c590152942ee297f3271dbd3d8588c3983592c6","2013af14579369e7822fb5f20f01268f3338141d4a382d5969144fe9c6a25ccb","6b7fcb23322518af97092aafb777aefa9196a1db215071fe8f96ae6f6101b498","a98d81cd9207ec9f1bd54d454809a60b267043dd56b165bdb6912bf9d63d4759","cd1aff7bdfa3af19aae3a21c958b932af6941ed89af069bd789f9ba5ad43377c","33e9caf10988726e5f7be53d2a4ebaad8db16e51b5f81e5ed4dbb0068f3a88fb","cc0d535d54cfec869fe4d28b9acda7e3427b25d8d84fdfe495021ad4cdee301e","f3e743e40c9a3b22de28624c8428e1df4cedc2fa3b3d99c546ebb95592ba1978","f8dab311b48db5e3b4361d76a629201098efdaa1b785120353f0ad221b263682","261b2daf69470a9e9d1ef26aa5e0c23f1611af380eb5e5f031c776ba57367741","e181f3ee5845726fa8077b39742a875d4fbaeb35f036765f95a77afcf982f989","9227c7d1182bc93c52d39d65472fef3f850a0d0145e1fac7388758d995878ccb","d90fecb86a618f308931b2efa45d7beeb73185db71ff184c1066b4e5f5900771","69af21e2252e417f1f26d2e6b1ded1f20dba11f066ce0690c53946f4369e00cf","fd4ae6100e700ea9e36ab622968f12ec03792f98f7da286cc44649395022453f","1adede2d971ad167525195ed811b8e0af2d17c728350689c470b6d5c5a3aa60c","4486b61dcb644ea4527e90b5bfbe7600b9ed50371d334a0c01e8fe661a99d952","dae0c9cc1c106f7b91a726813cfafe489dec4fb1e3f4f7c684d14e5f99f96b95","bf5fe5a9da7069bca0bb83fdc31ba9f624ac778c3ce25a06a3099d83a92a4858","7a0958a789740f9ddba6b7d80fbb1ae4d97da910970c1d59965ebc80304ad22c","54c9f10d962306a39448b924e73e631bba8221b5797ca4b2dcfb00dca78827b8","7125d9240eb556853fdaf7c892b60c630f6d01bd11580a32e9f299b0b91ca4f5","b339079a6ce4d7e86dc68ea8af379ee8700dfb205da5546ffdd3cdfe4be43df4","a97af3b565a5649c8cd983df40878da9a4bc080cf1a918199574f82a25fa727f","6e5996003f6555309d988ceae5b9952f3de47fc6e5110e5d64c612d4f37cb490","f07a1939b55120050d3fb95633ac6f8ebdb594e63eaf8f14c123480cc4bec03a","325e48e1f58a88830852e00f4fe7c515afef925a1254975a3ba9c54c9156f3fb","79caaa6f2db6cd3a1362f50b48ebae937a234b421c66516b8c98282b7d713d12","e7f0aa1f6c72a395ef5ac8749c03a31b00ea527abca2db5aea63804fef2dbb72","edca8fd9f3960acb4223f2a596d1f944320193afb076cb8b96afc19fb83144ec","511a250228acb3131a8c9eabaf45a53c3015c8a5abffc1c2e8b37942b779ef31","496927b9d1a6a7bea58453b9749b878ee1040ddc22de6db6e689db11ec111ebb","d17c142d0bbf9f20c696ed0224d1740fbbda0b0211225f2e48732809e15f2505","67e2a7d5facca25f1df14bc4fa4167337405fdc98b47df49b98aa2dcdaff5696","e16f1bf72fcbea703f1b9dba81e6faaf65e29d24128f9e09504c9a862f41c9d1","2e47b85f55b2c6606b137b4be24ea386c58bd9621370d2373e95b530c955102e","1f9fcdb7e24f1f9b391bb9200e32f6353781a1c604159a6257e9d8f1df7b9bee","3c788378c8c8d9525c168ce0578d536607896146b88bb5f6670ab19834be30b3","d16aba532f5391ccb1a61f90cf8e904ecabf3105641c398df0f4a79cac27d472","8d3a30c921fa2ea91d1bffc7fd9b8c42dc74bc819c503208122bd2e84cbffb5f","e4fb93fd2ec72ebaaa1e657435d607ed155da0005e6e30c440257d9fa877d834","ad103c0c76b809a8830ca4f9a8c8cb43b53d578bc63dc8c63b1342f1de90f8d8","7e15e2f23da806eabcf4bd0f1e48d7a5baeface210f05837171b50eed7d2b894","fb5e2afe7c4f988b615085166421f8d88464f6234abcc740ed861eccc0e17ba4","652af46b250ee5144ecb0eef7bcfa1647c88fd170cca974bdff9bb315f349f52","790daabde36636c46a264b1628f488ae49b2b378810383a1059ee722e82ceab8","89a58d0ab59eec78a6b6532e5d748f9942568891619633c890638a2912224ad5","9af24ffe92056dea7acff1dee779be364ad35e5f9861ca417d17bfb447a0a230","8fab0b106acb9de629cc3f7bf784187cd59d506d734917c4f140d02f0dcd167d","d0ba3b6aaef0c96be907938b6fb2a3a04a5db59de34a40f7e426bc7f10bb46d6","d91c919538e393ed3c649270a73f239ea7cd9f312dcee7dad037869a6eb0eee0","fcf5f4ff294643e6ea5100d09f40668a3a8744b73b8f1c397fac4b17ccecc72f","af3bebc2d30fe79abc9a505bc890d16af72f8ea21ec59009e9d57c2d8f6e0b01","784c657f85bebb1a7d94ef05e10f1cad4abdf32798203ef8631f7c3aca2390dd","e488fdf1efc9112b9ae08aaf2be027c3cc5603b916582c45d73bb3885728543f","a43b695758408470608b548841c97ad3827e453fe81ad835e29b9871129785f7","96dd9a7f52627f94b64da26c1ce05d2350941487861c8c27a0014c67273c8a40","0e754d4ed9a6cd6c131515ba94f3f1095fb10ac3cb0c20c2cbeef9e895f924c9","72cac4a4359c6a5e2e5c0ece767455797e46871350324dfe42ab14238f675729","cd091878f6b6994d9307156bda8a4419c7c41c524228d9e830f5fa618d70672e","bd3bbe444bc7cc28757c7669fb186a9ba326d4b65dbf99e18b2b5b9ca66edeb9","b10c73b3d7703d2d870d35631428cdec737d4bbc06706b6fcc6f6e058b8e1594","2543b46883befbefe10c2de9f3a0e7809de7baa09e192edda748443fd15d38d3","01cdf83ab596024078a6ce08ff990770326ceaf16f9081a8e369b9bc5110cadd","bbb9a17f2654caa1d34f49428c0e48ca0fd0d9550f5f82da6f544c924afa17b3","a9d8ba2c15cd99f51aa291034a1afff2f67f1f88259d2162eddaa25e3644032a","affa88c9484982a9aa35b30480059dadfe98c3bbd92f76513ae2d1d7e68096d2","3436c4b40e71c333e253578f6f3176870d4963d5f4ec14862ba5e40794bef8bc","a02f786598d002e2e42854d9bb4fc5a4ac03589538055a0eca03c9ec7ad35457","348863ce75f819f43557d134e5e7ff11a8ae582ac879349cdf9156bb696012f7","235ede03bdeeb87ca21b68fb1398ebc4749a924e2a7219977b71bfc9c51574a7","ec83a163716e8a8c2324e3f6b64c907ba7e5247b43df47f52edef954232c0211","15a76d1390ae38fe474023a51778887a6e39cc4204f65519a448ed7d1931275f","d7a9a81362adcd395f9db48531a89df84461595d189a234796e85ef983399042","7ac3584d37571a5ba61326f50860d843072ea95673e53d23b0b684635db9db00","96354248b7b7fe792c9545b7b153ef20763677692e9baa9aa6d1afbb17376ba7","5dce9f1eea7d40ad9f10295dff47b7de6fbb24b33858c0ff91aa75043e9899d3","d324bb068a3a98f3d7ff92eed388935a5ed4bd46b7678c5bf057b5a2ee9416d3","fe6e76ab5933ca777c6ce422c7023d44799d4832a8c5ce35e3592c8430867329","cc5beca247a7da7286a82c0f3b84686a922d0a402ead5d11b9ddd0dcdef5c762","24661a3d44d16268a8ac8260f35651526c54496ed5f29e559c066b7b7e6776c9","7c7ddd5cfbbab70612c216ab1d1f982468118fead1d57948ed31b41cd692c2fb","461ffe558e162cb3e451654eed59d0090a267818fde655088616be907007d654","829df07ac748fd372b8bcace5b46e6ce0420d2fd65c23f64b86e8a099b69a21b","162ff76724612621b2623b71139fae21981b276595c5e93a909b464c6eaf6310","4ec0abadee52b5287cba7929ce1c34e81a67a046c0299908158497ee85ff20b7","3b60b6d30d8be5b573e75d148abd155fb74cbce0795055965ad505afa4b181f9","5ea0f9746d216da9d45885462fdad43ed26dc4473ac6d289a94661c9a1e7bbbf","96198fa503333a1856039319ad4bc45c6e32afe2ede6a050e23fee2127139a40","e9dbaa3c845b96ebd98a7a3c59296fad4f5cbe4c2e471d0c54a248dab6d575f0","63c5fe7a04273a69125008737aa3c18212a1276b3a2f3892080c346cb589a716","adca096d8a06e8fe1f6f8a1d95dd176e0ec2216f5dce683c9c3656a9bb1e1f10","fe5cfccf2b757c44e7251d6ac822f4892d63f0dbbab920da4f24b893e655e836","e2a5e2a231048f1b0a8c6c123d524adeb3eda1464ac2413fd039cf5afa57bc54","8701130ab14da66b4e908e13c3ece584b420399cc543bafca971c414059ee5e8","811e9e98ddaacadbbcc92015fafd5f5ce0dbebc14f3536cbe225094b1d61f885","f7c8e5f19d7159bde8f8e9c6561c6e517953457faa86018a7f963b72863380fe","c0bc4b28c78bccbb158fb2e8b3e37a86fee5f26b6098a857befd864790da7cd8","e7b604762369c8fa5ffafb6e238a2c7af296e5a25bfa25cb33191b525f064cd0","dcabfb44bd25183c919819f87428fc589b20c3b9586825ec456f94cdb67bd316","41c39405eb8d94777d8b30d2bb295c258391ac4a45deef8d2f569b29bb82938c","f5786f9b0a39c790d245b33436e75576990e41e995e1fff1b0919833a57f4357","970025f12906d26ea3c1c381199eb6702b9c8cb0bec44edc02e86e004cf95eb1","8dafc03ad3ee7acabdb9254c702b80755bdafa7d7548cc6ffc21814e83055abd","e0dba6e973edfd7a5d8a7307ad1e6ec014b51fb7dac507ae132d1f9429016252","cc2f61e4781ad29f2aa93d4850de1b1d8313f242631f10ca17cc99411eb63022","6679676c1dc90d9c371f3de8430bf070ed36d2677d9ce3d2a336b54c5c40c2d7","cf2b168364792895c95f8f98f8fb662f07787e518e6d25b0f7c4aca9927a1bb5","db115e097d9ccc281414e7cedebfb0435d5bb14022f147331fb1bdad09404885","aa78c2b93bce87b73fccd6726cac3cac4f62927460ff3495f2a05553b4c04d3e","298104d50f65103c256ad79ff5128141000e4544a6afaa998d3099bee3975b84","a99f5ced5c95d7603c94c66a4619dfd0e737351bf20757de516b7f8ca193cce9","341b20f291eecbfefa6760a69f7f3f18b2094edcc794f4e78e903a5f0dd86fa6","238dd354909fc4a682e0cc4bd0d1eee8ba03197a4efa3cb284e502355eaec8de","8a3156a33e38b19f00d543a9f7a96054a0a4b051533449fb04267b4e533f55ef","bfd14082a4db87c2847135aab3d617ad7b488b3e65ac82f1620742548ed630f3","e2aa5b5cbc067b485de95616efb852886f4a1a43685ed7ea0ee8e08fec961cb2","3d6d27c275808a7e8540b1778a5d3808542518acda03f5c1ea4c9c5831058ea0","f534c1a02cd756679611ca2b36431b51715a0c59a070d413e292dfa23b9b5c6d","e26cccd0ef5654714877908c1674bee29a8e53d60c8c2d82bdffc12cac6b0fb5","ccf581fb8928f37fbd6509a7d8fa0d32156fc4eb414f434bf81cf7bd6849f7b8","69b227120a5245cddb0805eea82a0bea405872bcf595d2fce9fc03dd16133291","d6f495bfd0020102c67a6f80e411b00b913a001c468001b7cbe8c592c748f301","eb4463ba66be74eb04aeba3bded1f485a6ee90bed8c28a2c2573f0c983834790","a76187885d25a8aed20f71760c116bfb89ed1612d125bc190ab25a1a7a87ed91","76d36099aa1a0c7ea690ee1675ac4dd86cc62e2643cd40c097899268f8d2f7a8","815d7ee4cb5383f94b88688b8f2d70ce3e5df4de147d3669d225f8bfcffad673","afc6a3b94e405b3ae5d5038fe66f3b2412300c93ba1250805ee1a7ad19964ff6","b168bf198df3af94f54863a77ca14dcdb67af689d4cd876328f7c70bbb7b985f","54a40fe6e389146cb444299ef2d7d6e4ec83b05a9df2e7611fd1d1d862b2743a","65ec140c7cde7edee0f611bc84ce505cfa71916571e76f5cc197ba1dcde32f2f","969a96cb343d30bd8a28bad66c77049aeb0fabd9f608ad82caee751082685eeb","b30e161d3bbbe3f8d15b0bc5d9b47d1935ffffc7ced1099fcd84536c906af711","3eaea558e36977f924d85b3207406594568053d7a52950081b7603d112edefce","4918bfaa32bc0f63380d84b19bf5adff3188797b17b055417bbfdb09bd531d1d","5750305060905aff115cc0a6f295357099856fe72d76a1193204c23b4b9417f9","98195f663b1c09572bf794bf2fd7351b05d5895ed471589c0c79ae2e7c7d6b97","355e8226f1a83a02c2c5dc22781defbcf171df314f6f3205318851fd4550132f","8abeb772b7a7763686fd699980b74d9895863a758f2a6b824edb3d5e5c235078","dc1e2e53c9935f62739ca37d9acd484e83fd25bc051e5a330c9be0acbe774253","71a542cb540a3c0d4d954d311937a4df56157b0797489ea5cb8a9e57f449aefe","49fbd0ae0b53936499ca6293450230273cf299e017ac4a1cc8de936d50d8e696","d1a6aec1239a47bbcbbc55324d6ed293f79d4554f6bb0e411e206a9e22c50aa6","4e81f6b7048f1022bd8dd7dc18e43b4aca8967c4ffbfc8fe80bd4277936e5be3","5ff6328b404fb34d2828b501bd16f75bf17590d2b03a66a469a0359da07a06fa","149fbdfda86091cc37779a6eb3f01ac5c73d6e6d34a71e76bb3702a1ebdd8bf6","3c1e92d9a7a0c81d02f016c47de63a39706bb0e36231f2e6727a08cbbef6cfa2","ebec459a7d4933732cb453254997b9b9e7d17319dd40a29946f985719e297927","394dec85f81a33891c71f4e6a1b9a40afdbe93210d5dec749a2791deb57df5e4","19d2786de07b0dd973e5515d84182d2734f1c1ecf602929f75b30081fb20fcce","728d3db3ffdbd649c96fd64cb5766993cc8cb10b4ef207403fb98304eba04f57","8e4ae5371abcf89ca3059e621b666f1a340db0575f0c8635431417528f2d6367","de4f5917a7bf2c62cd3ab4c171620fd6e88a4a92902f02c0808ae67793e6baad","8f09f0abdd346cdbb1e545a44c85dcefeb047d07080628562a328d3e88160beb","4557c1259460570a893f9adb1547b5bdd19948497740c53fbaf654b20ded5855","ddf8a6692f74ae9ebdece687ec1cd9ff63811c5af27381b40c7996053a1b0504","de735626154dab7ceb24b208728b7461aaa5ad8848152ab0b39192e7bc0aa4be","c6296acd69aca14033c815aebc1b4c7fe72b92e44145dfe6432fe855c8c7b463","fe7df3fef3d25c0455e7cd4f36fdff7ad7b4d2163e7285a74d6a98a6cb48a282","7e9bf7c76b60c4402bd48996bfb0d1fa552a576991f9f73dbb856d15b0346793","29199bf01375b374d516e5c8d5d8ce1dac3c07cc52b00eebc0a7ba7b05baeb1c","7696d51d4ce2f2f21e9f73214396bfd823bee6c57f65d39e6a2264c40dd021f2","4b74f4072dacae8ba4f21abf5d042e5da499d027b0aac8e2d8f42f5f591453a8","d3b884fb07719c9457497fa9d5146f7977fed29df303347ff4175f630b903fcf","665320db7cce83346ce47ab3baa657b3115d796d28d8f778a98e7f23962b1247","3f471b5d1f378519b8276a869cb746d5cfc9b2bf4d7e5cbd0bdec3f9b5f81fe7","4afef388856e350489141ad7d90ab0767a02954d53d953f558237e04c89b5d0c","7b16f7f12771f8e25117321d1cb607e06be688ed8db002fe2cb13b716d0662b7","9412339ec64000a6dce5898fd848f025d31ec3b446872070cc041ade876c0c1f","726bc5d2505bf6051e80ede05a576e21d65118c077a8407ef4f9eca8d5445464","05f2af853ef135671b9482077d19a2935e3d924debbcf2f4803110bde114f113","134078b75b0105535f6164680bd73f88f9ddaa84b7e0126aeddd2af7ea27bcb0","264f0b10beaa4141a6bf228d2e22b19ff7baa76b39388da319bffcd1ced741e5","98682512d496bb618315de173d7a25ec363676da2457939f42b75a23c5acab87","1471ca4439a9dc9787976d1c9ca2b913908f4029465c87372d2efe2069c375f0","54b8abfc4713160ce97f91758ee1e8a547ba67c7328177d5e4c40613a3e87f79","60894b9993d7e1a7be37933c9bfe96b228ebc206cada93a44bca9d30e12d8d8f","236ec9d640fd6438a08dd2be3fb739352f147de529b4aa1953e4d4f74d6638b8","e75ea841bf22a156a7ae8f95eb7b1d4479da8c291019148d0a643027356b76c4","4111a7444998538da1f6f76378412547281e30cc5a7249b32e7402f66f83a492","4b9a4b3456012104409fde7f7631be98068daaafe1c49b627b8d92f033960b67","7f7840713032b2ad3bbc379ee2d401489b4e563294f7c87dbaf2424a6682beb2","b52b80732805d494ffee704b80f689772e1db9440c1728b907f7b25b3328d5ea","a805a58a4c72c7d513295fa7102284dd9cf76c1470e1845a6d7e9afa4bafa609","724e0ca25a06f553306e33a45951c368346cf1fc4b26b8bf4bf88b1479131659","b0880e598b7256855af6b9ea2aebdf47372444114264b56da9d25ea5f95d064e","3ff6607fc3c3a85814ec3d6e05e358d99773ee7c7b5e9deed5e086e39f5372a6","62247290540b91ed85258d7e8c67c7786e38bc1111429da9fa42b1b34e4ffdd2","656ee3f8184b5dfb87200f73350e57827dba056130d15064406487c0993f0e6b","b14c19907984b69ce25e011e6391ff6a150bb31f344d643f2a3d5af9aeb4ba73","2b77a0e88653109c708203a50fa23bb50406a9c8c7f61883c92e009f778387d6","2aa50966f709107108e7ad733c129c81f9e42731492948a9c23d4f8a0de5ae1e","080afc7aa193ecf03c78afe2e3d81cc3b18fa482f1bd955b4dca67bcbf220eb1","7282df0d72afd1c283644f5827159ffe0c899850fe121811e6e7e3eb77416868","07c70d4602003ffd9f23f8f6fc2693b7cf024a323a6d6146b11659105ae588fa","79eb7464a0215c82cf6fdc55b2378dc0a3aed416f03dc647fb6956975d446ec1","8cd341d72d1ce25d33dbe1681a9a5f27fecfcf65d426a0d0bb80ce97a1e37d50","4f750b488d0d1019bf8b6651e68689debd6106312ca7f4fca22627fc2d0acc04","4307994ad4d3a8d842a7d7da76f45f84e5eeaf1580e9b6071dd5fa6b8b21de19","87b54711b1c9791dd95b4ff88f814b489089f5b128f29e8a5fb7f6b8123f739e","4763437e8a65ec15310aa20a4ec288eae3de1b94b9426336ac423fddd482d70f","e2398ace0c73a5da036dbd6cab98008a251c709c56f1b665b6202e99ae3450dd","1a59a1ed95ac47cb6d1798a4dee9088b847f41491e57545e788ede35ed1204e5","c89ec75e2ebb2f5c2dce2d5f85ab59951cdd217748a49a6e0bd102fe69f5eb75","e653e5173f22f243892807fcd85800dfa4efe84be40e0ed1cccd15d62e1c9da4","973c290e94835130e51e934d078ab80e975a7bdf5b9563f5fa8de080a06c588c","5abc40134600b35d15fcc7305d5bc9e29942c64dbec6413137b55e77b689da1d","85c3303460c6339a77ec37ed9b7e06974f6d8e1351181b3f6f0c5180e0e7a76f","88d761b9b53ee5aa4ffe94d18e1c05c31ba8778beddcb8418f303c184f4f61f4","8ad1059fc2cf09ab5208fdc50a974a1a0c0f3737d81c2aa0718c583716b49f7b","ded7cab1c0c297958efedb1c4569674117693e0ebb6e8a1366cf7a629ba490c6","997c088bb8c6e1d869a689a3db0157d3efea26fa3fe49ece5f01044321949769","aa4d9968e228a15f4f93ba782c05f19de5aab2598ef8acd819ce72d6f4c9953e","1c4420d12393decf20734fff3f09f44daea5433869633ed11e9fed9b316523ec","25e74c23ca9d123ad4726803694ca249b958270fa3e98eb3de7f339f15241a45","87537c5c41597d3355cd28531f715bcfa77b73f0622d49b28b6064b3c0ba0ab3","56acbddbfb96d3e9502c73d87f216fd16b9954ce7fc345adaa51a054bbd548cd","ea5f8a7e470eec67d9b3a57a311393d9b8146d59e7d3965fc2a07745aabb50d1","d75a15490d5dc9b8bdd209200429a4cd31c139b1503e22e1ef743e6d4fd160f2","fb238a904625a2a0942f8f0aad2c96d5ba7b684b59890599a10d73c1bfd3f771","35c91fef4484772dedb9c253a07ad91912a8e349838bef1e7c85b93b6acd39c8","b92d1f42b729031910871b073bbf05b8d68994a91dc43a7fc64f88abff16db54","beaeb7cae58c1b074e1e5c4c0cc4e205b1763f0e9f413d7062951e1cf539b450","2059b7deab3beb764a2368200ead025358b48fe470bd6850500785d94c8fb5cf","da08b48bf74446b6f988e95f501693844d59d445487d89d2364b8bf431c8a21e","17c4db14970964450f5bdcd1349e6f3035419db7f7f9f88ee966b3b34cbaa8b3","a9e6f34151c8629364892059c1689e2f99775b3642a24854d0330112d6892cff","bf5fb8cea51021d395c45a092c1e97534d498c91812b99fdb07c658cf5990585","4b46f4712ae966996b2cc81949d482063887c55478706e25d942482a44b99b71","71c1350814b0e0683b2725e5eb7d44abf44944d23d5b9ca215cd7e0efe0496e2","93b24ca76698e62732d72800da132367639a4426363c821338bbbd7cf6b64443","ecfa9ce3a5a37d15b813065e8a7cdf677a0f493018e47ce59815443dfbb9c910","83e56d3337e1a6dbafdbe5a2502a84c330b1a328ed2860d689b2ded82b1f5c95","e630f8a3c49d5db0a8af774799abdb8f19675b940a6cfa25eca35e5280709f28","413c03b679ca5bbe6456b8a38ac999fffb3834d0e4a9638bd186533d629ab8cb","ad41c7519202d7daf1ca369d8e85e607e5604474a25e6d2d2e75730922e9ca76","137a44f0f62468569f6cb54ec0ee289b9694bf6acf872c749f20a80bf9e5b991",{"version":"962133e81ed097721e4b7e9e7dc2d76df05deb5f07f5f2288840b4fe668efd30","signature":"d302da76ed5ed034900cc612ce0092c4d2150ff90d628e3baaa43814408ab444"},{"version":"a3f113bda985865f0fff6b8ef06cb4a1de6f08cf9925cf5f4e0feb160a627cf7","signature":"7a03094a8b9cc223b933bf8b9a3fbb4e868d3738cc54eb31091d98bb4eb1bc08"},{"version":"46398af9458d0dc8f73d41144639f1821073a2f61a51641f77a2d31b55848619","signature":"c68768c6bb72fd5d1ecfa189f0b0324264b8e7f79235e2bc0f747a4d45da48f4"},{"version":"c715c283d860f353983ddf5e4eaea1d7e6131c028307b4a1a7c6ee80c578265d","signature":"71450e44712a9214a5bd86eaa62e2c896119b115a5a92b7d7099bd5a85cf4a48"},{"version":"14563e1a6ab8e6ac49018725f12d183486cafc8f83256d9110106f8d2a596f6d","affectsGlobalScope":true},{"version":"f394a9d7f647cdb62ccba2d7c62ab4b8690e86c7bd826071e6108f6a9a880875","signature":"f6d6065fb6f9f1178450120a5ee33e342035cbd7be5035ffa01187bedd7b5be4"},{"version":"3b0e07fca438a6c66b48e1f097abf40e3176a2b3355bed940d773c0fdfbe9a0b","signature":"a276b52981b0cdf8752cf3a10b33a515a4dd920d4ce08f1bbab6d8829086633e"},"ec676b2cb5cbc5532f3070c53eb560ff0564bd9843c688d6d6a488c1dfcdf48e","25488ec0923fa0fd699444ad2456ce5e8b8ae66f4681f3c27da4d15dc26e28b9",{"version":"2eacb614bc17de46077ea6f39f7dfde557ac86c40a0867eb60d642cc965abb04","signature":"6f86c2f3c68d9e9a765584a87fdd4ae1f37d6f00af22f67af35f64de858d633e"},{"version":"6d80e9424591cf47faf00b1cc2e990ec0f596c99d6f47f21acf3ac208c4ff207","signature":"1200bed795adb689d8617a64b826bd3ea4f25736c23c5bd83edfc80abf8300c5"},{"version":"2cbfb22a1cbcc21a3ba4119b997db81454f6b0eb600832592dc74bd509739668","signature":"d50cd2179a1ef4cacebbc3e757df5d07344a7b4197f6afc1ae93d522c2cf64e8"},{"version":"3e5c4f3c08c796abed5b58e0aea84931abaad6ff999e79a3851a11a012e5b3a2","signature":"d3423a938629b9b41559ba1fb3997e0c6d68e546f1d5b332cda293c3f16da720"},{"version":"c816da1e9aac5af1ff0364a36b6c48b8eb0a5f77ee4acdcb2808a070cae420f0","signature":"59d19b4ac45de96857810e4433ec0d0a107ef6996cdad44979ff0586c240913a"},{"version":"c584d3c4d3b195d93f5b5ae2e0d636b0426afb3c8f4ebcf13b6b1ec10f43f357","signature":"a448522906a0b415917257c423e5fa3e7e72bcc6ece9d812b980bec2ebf64ea3"},{"version":"67c763aacd33876afc6a3db9ab77e5e0704d4b5553ce136672de624344f4ecda","signature":"1b5b55a8d4562ad8c0dee5bd9ef94caf90b59ba8e5e739034d4fc349fc89b41f"},{"version":"09b7e509af380a1d32602acc844227a0586f2291b8ac91a430c9489a2f980094","signature":"d8a3b41d267a8e12704f6004c29b92c3c5164f771861ae2c8aa0783093f0c904"},{"version":"02595134c799ccbdbe7de2ed806a800e7e371329ab7e2517ebcecdbeea3a7931","signature":"8bd25484a170f02397df5663869b6206a20099c4bd4eb9a76efeca6448727911"},{"version":"f164846baa71658b62da152a6fadbfacc3f2e358c712bd5ef41b3cbb9f56b288","signature":"ee35473d793e47dfb8dc8d8ec7eee42c60d4846fbece4de84e844e6d08102a1b"},{"version":"9f6bf4ae00a700facadd7c9e8783f863822b113dd56d5ff0ed9c7febce65d4b7","signature":"946181b3d37504b3c7bb8217e6ab1bb6a57b3b1b3ce0060959044281d0eb1446"},{"version":"1e80167581f355b77f1d3683284c36616fcc799b954dcc16933e21def88bcf9a","signature":"39d5d2dbd8e98ec7c7aaacea54fc7d50ccae6a7741bc10fbdacfdfdb69a3c31c"},{"version":"c49f53e402985abec94ec7d9120dd1754eb4ca082cc387d20b68eee3d7211b80","signature":"00ddc5fb303f5a507fe302d3f0e2f77828931205afd29b08df1f0aa7c670e81e"},{"version":"f8ea0915ad8c57bc41a83617c6ebe48c1e3e67f4acc6b1670820adeff9bc0fd0","signature":"a80d1184a2e8f3d5d1ed19520eb71af94a1dffce793df9622ef85d624783cf2a"},{"version":"589ccb6daa4c713f2bc4a938e01cee8e4e49bb1613ac4e8487d6e0bb31e22aeb","signature":"1ee1752fce9d54ef31e0ff9f74b6337e1612d76adbad4f60767e0f4a36dd3c63"},{"version":"686729a0eef3eaa9dcee2238a36ee531a0361f11d8e5f14d5d12ee9312e57f04","signature":"ccca5bfafdc1eba43e0936e3fd4dbf3055757cabb358dd99bfabfc057ea1276e"},{"version":"b383fbf8aaaa46e3a7b14677068026fa40570f6c73cb43560683ce7fce9f84cb","signature":"0c497c46c0b30f384a2e458127037947566326bdd0314217021d7163ec4c3a54"},{"version":"5468406fdc27f582b9ce7c9921ed3b4afb502187c7f69907ccc278a7f1ccbd70","signature":"532fc6908d4547a3c74ce7b9bc47fe5c1d12bcaff54a98d57a2bdb54451aa933"},"b02b63f7db52fc7ee6468b53136fa64a076e2e4757289c018a8ed563c619261b","5c3038955e6bb4123497a184a5d878b09f5a78460df8685e2c81d30f1bdb7674","2153bfd12033593438d6a0c501f4872ebeeb1a9e5a9b3880fc07dae839f0e258","db957fbcecca7cc57e43b940f8666bb9b7400ce5c4a9fef9744c2d119be481dc","142e997b6952fe35c442f394839761645827cebd4756b4ac806969c329adb625","58850317b3ea1009ba2daefd8db5e8156a33171f2c70c816577f2b978856d98c","a69c4caf5a054cf2aaddf5e6cec18b116deb8ed1415c4e586a384e8a6596268f","410e831d2b8373bc7c71ec5f87a5b56ba8827c133a877aac65df9694b5695084","f63fa4b4d9b9099b44af81928cd7c80ffb318930260ababcbfae04a1f869d9fd","1542fb6a9f509c4647480e13f621d9de8bafe9ae45b54b59121ad4ebf21cdb9a","829d0233ef1650ea7917817da6b4f80aeb2668e87c2f575e9ff01716025032b2","8e4b3a5b64ebb509675722909ab3dc0b87052456825bea90a5306fc1f9e68b17","96a60edef70d230fc1f413e6ad8dbf667ca55dc626603bd1bb2a1e2e7d46e0ab","5953adef9f8020ee8cd1d653d0b4954201ae25627423ef8c0b3700da9d0869ea","ab420b7d6260eed591d2b715562f66cc63b01277cef44e0da8455fb95875ea6b",{"version":"b531743065bdb99a534c7c769c9e71ac329265f3ed83191fb0182209ae784bce","signature":"99c7c4ca8f96462f10f663c52e23d5c5aaafbbf136cd12d6f3d5938a21fd4376"},{"version":"8cd56e75fd71319b3e708f10d9f2f4a8ee275eb1f854c3c801df4d62e1578403","signature":"cf1fe75a02f91625c5ba169259516ee5e01406e4c1c58c323adce34e2c99c690"},{"version":"4a4214aee85054dc65b673f2454348bff78aaf48f13ef71350c152a93038e694","signature":"fc2e5c568ccc7afa269cb1b2706e384ba08ba98771233f5216752495094e1d51"},{"version":"10304779771026da436530224d69c7549f82037e3eac57b8f2565645b3063a05","signature":"77d1428250b9b413c6282f6908df30b2293e61d014e27d7be81fe943101fb4bf"},{"version":"07bf13c07a20b8228591c71612598a9ea4c973f0c37f95086dc6d4565aa440d5","signature":"0f7503a21104316710a74a551244c3ee4b1ade8aee4d7806eeb1315eabeeedb9"},{"version":"07d68833fc9535e3422fe5705446294915f3887f7b1086161f037cc031618130","signature":"cc2aceaeda6a5f71ce8b55361d7b316bb0fbf7feef204c6c67829faedcd52756"},{"version":"4a6ccfb2053c31045d276deaaf75b230947ab48a07793ae46547c5495f53dbff","signature":"b02ae27dfe5d1b7c862d6fc79b6bbbe723e2d8a8a01e10d57e5c0325ce528e94"},{"version":"fb0861dadea7ef8fd5fc37dbc7d761c59fd092cbe505b03cd901b9770ee1836e","signature":"f63fdce169eada3c8a463e9e618cf86e381c6e04ca46f0de4aa6845ce5992830"},{"version":"4627626192565165b01f667e5798093fa48cb9103f3f25cc439a06cdab229467","signature":"fbc3b32c432ecbd960d1dd81f8074b2bec95f66686b168ff54130451afa01f27"},{"version":"3edd48ad366febf094c15822a7abc9544c2207dfc78cf75ddfda6add10aa4e7f","signature":"4b9f88875eccef94366e0bb5144e9a22be16f8c9b4de9839327c069d3d335663"},{"version":"fac25c0e2f59174f4f471d04d2d8ab01a3e695de535a017b5c16a10dc4f2866e","signature":"dd5401f6113912bd8162a157bb0d19be36a0aecf6c67095ef5e2f9964f563c4d"},{"version":"da5cdbe8fda04d03ba015d6120c94c41387e98043a4b3e659f46846b1169013b","signature":"9aec68becdcde9c0e51ef276e57694a38c6b8096dc9cf26f3e272c283691553a"},{"version":"3f792989e90048608f79068deb6362d78e739b370ec8aa0707e5dc7738c3824d","signature":"a3ddde8b70134db2f84dc445731843c74892bd3d505cb7f93fad594db115bba1"},{"version":"cac19753cc25ac223d22b7b16333e261eb328b877dc4a2eee9dc73c8cc6e8fd2","signature":"bdf48bf7e837f41bd26c72688ca2bd5b4ee26833535bc7c2f7eca40c1375766c"},{"version":"d7d0c327ed89f294e113bbc46b1527d796c513cdb84a3b0df8c41daf52cb7762","signature":"7b4ec82830ec4a62fe42098f884917ef5ab399a38b284d1e882bf4fbb6397f6b"},{"version":"40dd9d69cfdf0d23ff90884f910a975c36151c4206f58d9ac8b4da97f9765283","signature":"647228471da91daa09beaaf50ab1f79f03178b1249536ddacceb59bea5759ddc"},{"version":"d2c0bbd94e93bc74ee80066f5d0d1c7fe7fdd7ff3c1041529b998978cadda606","signature":"7ba31cfee7dbb2d86cc2f985ff0b5ff11a92cbc3e29264f32a0d8414fc24fcf3"},{"version":"49014fb4afa4d30980b83d422c9064e7ff9a6af09f0aa9aaa00a304cb25ed3e5","signature":"57bf23320971bcc1c9cba2cf12dd7c918aaddc5259b8b131fa2d039b1d40d9e9"},{"version":"3abea28c81680d4f1b7f13c8f048c309af1b74d36654d66f32008852f88126cb","signature":"e0bd9b1a179d0f562cf274c19074fa593fd55ceb31aa9401dfde826ccac72993"},{"version":"8e96321e94e0bf767e3b621e74b4560256817ba93010f1162a4879558bb7211d","signature":"e971455d52c072c5b005d021f05b86b10d833c95844db22c9f676ad5f8a2343e"},{"version":"c0960b77393b85216ee6b81dfc02766bfe5c204e2dc23d9584d68ceb23dfb383","signature":"8672b3c3321e94dfe031baa9e406b7750385abab026bfb784c352abd43c8b09a"},{"version":"80e109f2ece7df0d6b66100f8aa48316f1584a4a7602764baa66e2b63402e1c5","signature":"11e338b61ff7fb9c0d65aa109f15f2ad59f427dd23d79a0af4ad39ed1e4bdacc"},{"version":"7a53b73908320659a22f031463e6c22573539dcf07a0470961f232b37414e384","signature":"cc49a32e217a0e499e2d2aab7bba9b852c046302468a1f927cfa39e3ff031abb"},{"version":"9d88473822eff1b1e5cd0303965523692861a2f0742012ec87b834ddbb5cf3ed","signature":"9ea3d0767103df70300d4dbaf9a8934de2ea66f13bbfdeb7ae91d93e813d86ef"},{"version":"4433114f6f31fcefb0381d4c1e75ac4f7d66add893583958c50db25904055578","signature":"9ea3d0767103df70300d4dbaf9a8934de2ea66f13bbfdeb7ae91d93e813d86ef"},{"version":"ac06534c7fbdcfd36e5ae43fcda5dfd07b6aeb75729b07f50e1f368aab697b75","signature":"31d936d4db389df30fefcde5c106e79c8e5938d79360eacfafa11af0220f3870"},{"version":"a477914e4e11e779a90dde2a1dddf5fd6c4e256cbde475998cf0af8996a0c947","signature":"37081bc9f8efaa6dce4f5faa31f3dbff39a3832dfb83824147a0e35e240f871c"},{"version":"c9bff56a1925c91534a2abbb8ad21337f8bca10061cf6e32ef90b7590b4ad364","signature":"ee2e0d2c32d9acaabda6a747f8d01d32fbe67a096a53f4c004df597af8ba53ef"},{"version":"900047558d384c5b86873b38d9c596e88f6a31986e083dbeb97d3a36b4abb000","signature":"b7fd9444c603acea920ae79efda549ffef144daa06c727ce672d9a1f81c8a04c"},{"version":"8b6e9004e08dac7eca384426a9548f898fb73fb55edc5fec0f965c351792be4b","signature":"d1b9d446b7ab6960870502527c772d1c705b35d2baca81008413ea3f5ad29701"},{"version":"77385c503530ec1feabdd0f5c7486ceed070d8649c36a2417e742ab34c667935","signature":"73978c9a9428c9a3a8c2ea8a42ed61411ef6e1cb145a369126cfd41fc00ada4d"},{"version":"ac127c4c16d20f2fd934a8bb842f54149ff69e2c65b529a82d9b58abc3d11594","signature":"415a98b3abcfdcc9fc575cf480cef6ab1e96f5809109e2cdae0d7bcd32d3625a"},{"version":"72ed87ff855756b118a16f93ab7c7f4f913feb2f672a5dec25f5092a75d08010","signature":"56333c10735d0e589187112c0819c284124acaa309792971f0099ce6c32b5c77"},{"version":"58233c9dc2341f1aa3cd7240465c30174a9ae3645526abe9c32bbd73d7be7f89","signature":"bca6944b3354faa28acb62fb697e6ee3ff16abaf43600cf515019a8dd570ce6d"},{"version":"1d4254e3b839567fa2627fb1d40ecdb86f2e31e39141df13d4207eecaa05d07b","signature":"06ec3861f179f74ad4b67d6cc63db50b282830ac1e14cd4a68971595ee7dc879"},{"version":"323a178de2931dbcb8167ba82c2fd27d60e83a0297da5af0088c0a710648c193","signature":"418e82c593c31dbc7257f00b6ba03d2f60b1d2ff10099820e428742ef0a48e22"},{"version":"42dc4d122231873bae981f2e461c3d2a8e715159d71d04f91ae5f8bebdae1a7e","signature":"61814ce3a1afa4ee7f2501574ba25ee657811ea7efd7d4d015ddd07b645fb1c1"},{"version":"cc5d918d642c292771508d95cb5ee19b70630e6af3f5790ae2e2a933191cd883","signature":"456dacf7f577ddd38e5be59a27f557fb2bd98f8d348dd5e019d9b12eba8f9981"},{"version":"880ce868cb7ae08fdfe02d90a7ab8bc14090ba40cebb541a80c3bc8d6ccffd42","signature":"f68a9b1bb95fad0149d6d985b04aabfcfc75bbeeaa624e443243fff06a2b28dc"},{"version":"5e9f5cb4e69915e7e1fc9b82a2aa8ccbde05a8bcaa41d6832e65f3fb0f4b2d66","signature":"dfa13f76889578edcec29926fcf9699fdc905a3fb99f1383e4f1be9eb125e200"},{"version":"6282e89e8fd4e74834ad25af2e3927ea403227cfc44efc52dbd22a7183527824","signature":"ae047223379f93ed00d91c1045932bcfb1d0619815b5e419fd23aabc1388a309"},{"version":"6beb081cd9584bc2b31e48c7db7c96332753e44009a466de58736185d2edc7da","signature":"d3ceb69f47f6c8e9248f73b97fdf239c30c5d798b2adcbee7c8c7e72f43aac6a"},{"version":"9d53ad34b89e52d654ebc20625a7467c29113bc469eb8a4aefec4f4e4654fea7","signature":"b7d9d77ff0ec7ddbb8c66393e3553dd7410b166248d61c30d41a462a6cf4374a"},{"version":"f39aa0aeba66e3bedb2332688913fb693fc93957734803cd7c8b4976b39ccd34","signature":"cbfc3f22471a111a244887ed628994efc3047bfc7f8b1343639c194e8475634b"},"c2489c80994d62e5b51370a6f02f537db4c37af5f914fcb5b2755b81f1906cae","913e12626ff2cb7da9ee1dda2ffb365f1adfc72339d5d64aca4bbb988fc980c8","b5717126992a2f5649478187cb466685e88b2b22e91c7ea240b63ef4ccc3f9ef",{"version":"1ff19df896119975cc998951fe34d47019e3fd60f0756ee639748d3d466e4ac3","signature":"991d8e46a75b8ebef79c039c9737f3934f3e83a8b982f67c756b9d184fdcf047"},{"version":"88611953948fa742e4768d513be281db5bb8046d8d40b563daeb69f527c2b520","signature":"af431bacf794e74be3eb0b5637f3a5f77b214b55eefa144c60ef0e0e42accc4f"},{"version":"df0d65967d334572873ab09b3738d97b318daf48e11f38069d1c6af07a160600","signature":"a13681c3bb0fef9095c792c054f7bfb47161aeca038c0e7e26456ea06fa28220"},{"version":"a56f7cf088ed57f3fcec373dfee7db9f0fc4ad6ef93d16a492cdfdce997560a4","signature":"f02fa3b76cbab8e2fb20702ad712776f314b6cde822b56cd74aae67ee050b82a"},{"version":"f7cd5869bf993c78edd34fc7e371313815c6bdec5809ba63ebb5f5a2068aa3c8","signature":"fbfd517f20480026e8d065d2a55d2fad76672ee2666220748a751ff1854def94"},{"version":"105be0c01275d5b8899a6d41244efc0e2cc94c31b82c37c2e791b4dacdbf09c0","signature":"2cd6811168188a04814f3ce54218afa55c5e8231b126d355b53a1803d1b4fad9"},{"version":"d723934a74ddf1a04abb1bfea68f96fc2ad7d6d15117a949e58ced2c9b77f046","signature":"f6db5824c9908750376490ebca8c789d715fe8717137ae84d1f0608fef58f506"},{"version":"6fc859e7fce048ccf3733fcfbffc918823e4287651a0c811ecf1b0eb20546d58","signature":"a6ac7c224f4065e6bbeaaa72e22c2e23d0696f2da8e9c8fa6fed2d42cb2d9c31"},{"version":"d79df5e0b13078320ae0ceb99198fe7e1aba6fca446a4336c2bc7a1662d39a1d","signature":"50e4c1fe2cc3a0c954baf29fc1f98e74a8092385c0351f051eed07dd70512513"},{"version":"fae1043da49c6d57a7f0fe2190d5c9eca3f90fa6d61d519abcfb55fcdcffd6cc","signature":"af7c9f2de8bd0e0497354f64d468226ff464ca6c7b4eb9837e8ef715677bdf85"},{"version":"b50261db23dab701daaeeb8d2b8f92b696edfb047951fad3b6ccc73b25bca925","signature":"20d0f4edee6d053087d9db69e22e8e3d2a80945856626363bd053dce17b90ad4"},{"version":"e5245231f7536c1ab065f42fed8a203801f8ad8a89758d68dbd52ce117b23625","signature":"6cf3313ab428de94803690b37dbb4c25938bfc37d182364b57f029c1855fbc09"},{"version":"dd952b4462e75d466dd3d2bc5e6362edf6e4203b0339795665d2e87521953cde","signature":"2072c720b25e3d3ae312fe3c2894f71ad13d7b7b96c914381d21b3e7110d4053"},{"version":"b0fe4978d1ac7c29b360c6e651f1cdd86f98abde7deef2bef103df9eca9b7fcc","signature":"ccce32ba9ded99878483b4724acfa3304ab1ca68ba74e7480edd868dab711df5"},{"version":"114ab766054d67ce784861853c5d1b9445efadf524a41534ede5ff1066cfc89a","signature":"0bb9de5e6d87ca1a0d4a443fae93b9f0f1d2e46d2d23191b72484400ce9cf555"},{"version":"70ea5dd5e2d22ea850412dc8a3a7f691acea264dc1efc5aa2fa9892f1d9cde5b","signature":"c2d3764057c6beea5a7396241a7577914560d2e0738fc07c568f7ebae32d1b87"},{"version":"4d9e7f04c7647620e13e2593227cdce2a09790077324ba2b498a16c55991e6cf","signature":"c4ed81b57824ed43e7f30cf08fd42a966fc80d95abf210f2a428d568a9f17851"},{"version":"df911b6166b3ecf373eb41f5862d8d954f32bae8d445fd6417bbabc8a5d662fa","signature":"021f35bb07cce1bff57a5377115d971a51701166d7d56da5a4498a091eaa2eab"},{"version":"d8a42d532c21f8728ac4a84038d250dfed5e92449a4b043f0896e4e5a2e2da5e","signature":"920780dec4ab9e85fe07c691fef2d1313a2c3c54387e47e046eba2162cd72e72"},{"version":"3a6b41ab301f6974e93cc1af255ea43dc35ac653ff1f8dc14b1ed36561c7069e","signature":"46792b4130d161bedde66b654646021017ccf85de05a150d6abd457def5ec587"},{"version":"8eebce9498203071c792574d08999be1112d816ec5852dad58c684a7a7018dc5","signature":"3d11f41d83b75d4964ce0932e9de9af6619c5e46aa3f5a2c69cf245fc06ebe00"},{"version":"cc6022e1c3ed0ef206fa510fbb8b843c515800f703f1ee8839f5179e89b806a1","signature":"3c716caf2ad9288f35021c2039613a752d02a12350f795828fe63f6f34c335a3"},{"version":"b38dd9bb515fa0ced061d70d64432f503a36198e19892dab9da10d34e3e99bb9","signature":"dcb3d4afc8ebb0798f859fc8abebbb3e989a2da246223a422bc8d226e61ac2ef"},{"version":"0aef6d495ed0901e42845dbbd6a968f7659d6518d800ab213d074208086401fc","signature":"adcdf8865b217900854448da4b5f554e733185c28ab5e577119badc25abd14f8"},{"version":"dd7a640dc3e916ff3e384b1c9675c000e3245c21dadf8dc0bb1f903797cfe457","signature":"7e12e9db5a3ec0ce0cdbf1e72f42473bd8aee83f7f9f02f14c0bed2d86766b4b"},{"version":"9650bdb6ce06a70bf520ebd3f513bbe5e6dd5937d03223817bde0a7a10fc05a3","signature":"dcb3d4afc8ebb0798f859fc8abebbb3e989a2da246223a422bc8d226e61ac2ef"},{"version":"09320cfb9414938879d2afa0358de9a44f7d7ae9704688f7cec5e10a14a7d913","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"68ed0d3dff7a59cc2f3e9413d532efb27720aeb353bdb7ae793cd80143c5ccaf","signature":"8de6d6ce331277cd0506f938b59581343dfac72b2ca26827d0c4e98c7ee6d4a3"},{"version":"2272160e53982cfb63625ca42b0552becc61dcc2adf4a927f9812ee2af4b0d28","signature":"e1b6f99dea27a71be67608366fb4959e3532c37ed48c358dae91d3bb4755abe3"},"0214f8d8031b8baa7bc3e7712c06568a5cd5fe04bb17eb6e8b9db8d9c3e14dc0",{"version":"efff894d0a1fee77a75f8fd7b79a9c5195001ec3e18dd03a790a2f31bc92386c","signature":"d0fbd172936da7b818a14c95fee527eca3d2e5cc50c36b519f780408737c61dc"},{"version":"99d5e54ad805b977a5f847ff186c9cbf6a799c523207c816e0308b59e87e0ad9","signature":"2d8b3cd273e7815a378823489a89a82910b21189ac00d3e1df19669c2b915869"},{"version":"3227a60363a244c44920b51efbc4f02f817c9d743c59f869ee7261cbc1cbe043","signature":"f3a1d9153a8db4ebb425a35984f2239464ff649a39c6d0e63c3198da0216e839"},{"version":"69049f28225687c6804ad624118c9be713ab55d2be4594563660d7f734aa8db7","signature":"56538a6c173dc6660691f01f363db97fb69ddbe797e39a6536dbfd30db9a8689"},"b668b7fb7c52a05fb9233a27ba5099a73cd8e157b037d67399336635495ab483","b25c5f2970d06c729f464c0aeaa64b1a5b5f1355aa93554bb5f9c199b8624b1e","069733dc220affbe58d9c1d1a93af3707bc515aaed761701d8741b57da4cb964","3051751533eee92572241b3cef28333212401408c4e7aa21718714b793c0f4ed","691aea9772797ca98334eb743e7686e29325b02c6931391bcee4cc7bf27a9f3b","6f1d39d26959517da3bd105c552eded4c34702705c64d75b03f54d864b6e41c2","6d829824ead8999f87b6df21200df3c6150391b894b4e80662caa462bd48d073","6a9c5127096b35264eb7cd21b2417bfc1d42cceca9ba4ce2bb0c3410b7816042","78828b06c0d3b586954015e9ebde5480b009e166c71244763bda328ec0920f41","16d51f964ec125ad2024cf03f0af444b3bc3ec3614d9345cc54d09bab45c9a4c","ba601641fac98c229ccd4a303f747de376d761babb33229bb7153bed9356c9cc",{"version":"44f372b501e58c4a02bcdf4772d25a1239abd89339a19e7c25527aa0cbf37c32","affectsGlobalScope":true},"3ebae8c00411116a66fca65b08228ea0cf0b72724701f9b854442100aab55aba","cddf5c26907c0b8378bc05543161c11637b830da9fadf59e02a11e675d11e180","3d2cd8f3047fff04a71e7037a6a4cb9f4accb28dbd8c0d83164d414811025af0","de18acda71730bac52f4b256ce7511bb56cc21f6f114c59c46782eff2f632857","7eb06594824ada538b1d8b48c3925a83e7db792f47a081a62cf3e5c4e23cf0ee","905c3e8f7ddaa6c391b60c05b2f4c3931d7127ad717a080359db3df510b7bdab","d8aab31ba8e618cc3eea10b0945de81cb93b7e8150a013a482332263b9305322","69da61a7b5093dac77fa3bec8be95dcf9a74c95a0e9161edb98bb24e30e439d2","561eca7a381b96d6ccac6e4061e6d2ae53f5bc44203f3fd9f5b26864c32ae6e9","62ea38627e3ebab429f7616812a9394d327c2bc271003dfba985de9b4137369f","b4439890c168d646357928431100daac5cbdee1d345a34e6bf6eca9f3abe22bc",{"version":"ce2169125f42515a26fa60977b6d56ae407f3462e28832fbf6a9013f6a828bab","affectsGlobalScope":true},"0359682c54e487c4cab2b53b2b4d35cc8dea4d9914bc6abcdb5701f8b8e745a4","96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","675e702f2032766a91eeadee64f51014c64688525da99dccd8178f0c599f13a8","378df8bbbb9e3f6fca05d58f644aab538e1062eab5e778fb0b83d41125df246d","d88a479cccf787b4aa82362150fbeba5211a32dbfafa7b92ba6995ecaf9a1a89","187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","c24ad9be9adf28f0927e3d9d9e9cec1c677022356f241ccbbfb97bfe8fb3d1a1","0ec0998e2d085e8ea54266f547976ae152c9dd6cdb9ac4d8a520a230f5ebae84","9364c7566b0be2f7b70ff5285eb34686f83ccb01bda529b82d23b2a844653bfb","00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","82251920b05f30981c9a4109cb5f3169dce4b477effc845c6d781044a30e7672","3c92b6dfd43cc1c2485d9eba5ff0b74a19bb8725b692773ef1d66dac48cda4bd","3e59f00ab03c33717b3130066d4debb272da90eeded4935ff0604c2bc25a5cae","9fa6b83a35e897f340858995ca5d77e901d89fd18644cd4c9e8a4afe0b2e6363",{"version":"0714e2046df66c0e93c3330d30dbc0565b3e8cd3ee302cf99e4ede6220e5fec8","affectsGlobalScope":true},"2a2e2c6463bcf3c59f31bc9ab4b6ef963bbf7dffb049cd017e2c1834e3adca63","f313731860257325f13351575f381fef333d4dfe30daf5a2e72f894208feea08","951b37f7d86f6012f09e6b35f1de57c69d75f16908cb0adaa56b93675ea0b853","3816fc03ffd9cbd1a7a3362a264756a4a1d547caabea50ca68303046be40e376","0c417b4ec46b88fb62a43ec00204700b560d01eb5677c7faa8ecd34610f096a8","13d29cdeb64e8496424edf42749bbb47de5e42d201cf958911a4638cbcffbd3f","c9ad058b2cc9ce6dc2ed92960d6d009e8c04bef46d3f5312283debca6869f613","2b8264b2fefd7367e0f20e2c04eed5d3038831fe00f5efbc110ff0131aab899b","08b428a44bc98005536a12456518797e9afe2a08e8b5d9785641713a54475881","3169db033165677f1d414baf0c82ba27801089ca1b66d97af464512a47df31b5","c6c4fea9acc55d5e38ff2b70d57ab0b5cdbd08f8bc5d7a226e322cea128c5b57","bc81aff061c53a7140270555f4b22da4ecfe8601e8027cf5aa175fbdc7927c31","3bdd93ec24853e61bfa4c63ebaa425ff3e474156e87a47d90122e1d8cc717c1f","5a2a25feca554a8f289ed62114771b8c63d89f2b58325e2f8b7043e4e0160d11"],"options":{"esModuleInterop":true,"jsx":1,"module":99,"skipLibCheck":true,"strict":false,"strictNullChecks":true,"target":2},"fileIdsList":[[137,140,142,181,263],[137,142,181,263],[137,140,142,145,156,159,181,195,198,263],[137,140,142,145,156,159,181,195,196,200,263],[137,140,142,145,156,159,181,195,263,304,307],[137,140,142,156,159,181,195,202,203,204,205,263],[137,140,142,156,159,181,195,263,289,290],[137,140,142,155,159,181,195,263],[137,140,142,145,156,159,181,195,263,293],[137,140,142,145,156,159,181,195,263,295],[137,140,142,145,156,159,181,195,263,297],[137,140,142,146,156,159,181,195,263,299],[137,140,142,145,156,159,181,195,263,301],[137,140,142,145,156,159,181,195,205,263],[137,140,142,181,263,451],[97,137,140,142,181,263,453],[97,137,140,142,145,156,159,180,181,199,263,328,331,454,686,687],[97,137,140,142,158,159,181,185,263,304,327,331,934,959],[97,137,140,142,156,181,263,304,308,328,331,453,454,990],[137,140,142,181,263,328,1014,1015],[97,137,140,142,181,263,317,328,703,934,943],[137,140,142,181,263,453,703,934],[137,140,142,159,181,263,292,311,328,454],[97,137,140,142,145,156,159,180,181,263,296,328,331,454,686],[137,140,142,181,207,263],[137,140,142,181,263,988,989],[137,140,142,181,263,703,934,988,989],[137,140,142,181,263,943,948],[97,137,140,142,145,156,159,181,263,294,328,331,451,454,934],[97,137,140,142,145,156,159,180,181,263,298,328,331,451,454,934,1019],[97,137,140,142,145,159,181,263,281,285,288,311,328,685,1017,1019,1020],[97,137,140,142,156,181,263,328,331,687,703,934,1021],[137,140,142,147,159,181,196,197,243,246,263,288],[137,140,142,159,181,263,279],[137,139,140,142,159,181,243,263,288],[137,140,142,146,159,181,243,263,288],[137,139,140,142,159,181,263],[137,140,142,159,181,208,247,263,280,281,282,283,284],[97,137,140,142,181,263,943,948],[97,137,140,142,181,263],[97,137,140,142,145,156,159,180,181,185,197,206,243,263,309,312,328,329,331,332,337,427,428,430,454,685,959,990,993,994,995],[97,137,140,142,181,243,263,287,309,327,331,685,959,993],[97,137,140,142,145,159,180,181,243,263,287,304,309,311,319,328,331,337,934,959,993],[97,137,140,142,181,263,309,331,332,685,993,996,998,999,1000],[137,140,142,181,263,997,999],[137,140,142,145,159,181,263],[97,137,140,142,181,243,263,309,331],[137,140,142,181,263,685],[137,140,142,181,263,337,944,952],[97,137,140,142,179,181,263,685,934],[97,137,140,142,181,263,338,934,952],[137,140,142,159,181,263,331,337,452,934,943,952,958,960,961,963],[97,137,140,142,159,181,263,703,934,943,965],[137,140,142,159,181,263,328,336],[137,140,142,159,181,263,331,685,959,962],[97,137,140,142,181,263,331,337,374,685,934,952,967],[97,137,140,142,181,263,934],[97,137,140,142,145,156,159,180,181,263,303,328,331,454,686,687],[97,137,140,142,181,263,943,948,985],[137,140,142,146,159,181,185,195,207,243,246,263,283,288,295,301,320,328,331,345,348,375,454],[97,137,140,142,181,263,450],[97,137,140,142,157,181,242,243,263,287,1002],[97,137,140,142,146,157,181,243,263,285,287,288,310,311,328,685,959,1002],[97,137,140,142,146,181,263,285,310,311,328,332,451,454,934,959,1002,1003],[97,137,140,142,181,263,310,328,329,331,337,451,934,1002,1005,1006,1007,1009,1010],[97,137,140,142,181,263,288,332,1004],[97,137,140,142,181,263,285,310,454,934,959,1002,1003],[97,137,140,142,157,181,242,243,263,287,332],[97,137,140,142,156,157,180,181,185,243,263,287,288,291,328,329,331,337,403,426,429,451,454,956,990,1008,1012],[97,137,140,142,145,156,157,159,180,181,185,197,206,242,243,263,285,287,288,310,312,328,329,331,332,337,403,427,430,451,454,934,959,990,994,995,1002,1003,1008],[97,137,140,142,145,156,159,181,196,201,263,304,308,328,331,451,453,454,686,990],[97,137,140,142,158,159,181,263],[97,137,140,142,156,159,181,196,263,295,302,320,328,331,334,454,688,946,970,991],[137,140,142,159,181,185,263,288,312,328,331],[137,140,142,145,159,181,263,333],[97,137,140,142,181,263,328,329],[97,137,140,142,158,181,263],[137,140,142,181,263,288,1026],[137,140,142,181,263,328,329],[137,140,142,159,181,263,330,331,959],[137,140,142,181,263,323,329],[97,137,140,142,181,263,326,327,328,329,330],[137,140,142,181,263,331],[97,137,140,142,181,263,329,331,335,337],[97,137,140,142,180,181,263,317,328,1024,1028],[137,140,142,146,157,158,181,263],[137,140,142,145,156,159,181,197,244,245,263],[137,140,142,146,181,263],[137,140,142,156,181,263,304,306],[137,140,142,181,263,304],[137,140,142,181,244,263,304,305],[137,140,142,147,156,181,263,304,305,306],[137,140,142,144,145,147,148,149,155,181,263],[137,140,142,145,147,148,181,263],[137,140,142,147,159,181,196,197,263],[137,140,142,146,147,159,181,196,197,263],[137,140,142,159,181,263],[137,140,142,145,149,159,181,245,246,263,321,322,324,325],[137,140,142,147,148,149,159,181,196,263],[137,140,142,145,146,157,159,181,185,263],[137,140,142,147,156,159,181,196,197,263],[137,140,142,147,181,196,197,263],[137,140,142,146,147,149,159,181,185,196,197,263],[137,140,142,155,181,194,263],[137,140,142,147,156,159,181,196,197,263,288],[135,136,137,140,142,181,263],[137,140,142,181,263,1062],[137,140,142,181,263,1067],[137,140,142,146,181,263,269],[92,137,140,142,146,181,255,262,263,264,265,266,267,268,269,271,272],[92,137,140,142,181,263],[92,137,140,142,146,181,263],[137,140,142,181,248,249,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278],[137,140,142,146,181,255,263,264,271,279],[92,137,139,140,142,146,181,263,264,265,270],[137,140,142,146,181,255,262,263,264,266,267,269,270,271,279],[137,140,142,146,181,263,264,266,269,271],[137,140,142,146,181,263,264,269,272,279],[137,140,142,146,181,263,279],[137,140,142,146,181,263,264,265,267,268,279],[137,140,142,146,181,263,264,267,271],[92,137,140,142,146,181,262,263,264,267,272],[49,92,137,140,142,181],[137,140,142,181,263,935],[97,137,140,142,181,263,936,937,938,939,940],[97,137,140,142,181,263,937,941],[97,137,140,142,181,263,939,941],[94,137,140,142,181,263,935],[137,140,142,181,263,941,942],[97,137,140,142,181,263,689],[97,137,140,142,181,263,689,690,691],[97,137,140,142,181,263,689,691,699],[97,137,140,142,181,263,689,690],[137,140,142,181,263,692,693,694,695,696,697,698,700,701,702],[137,140,142,181,263,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,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684],[137,140,142,181,263,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,930,931,932,933],[137,140,142,181,263,447],[137,140,142,181,263,441,443],[137,140,142,181,263,431,441,442,444,445,446],[137,140,142,181,263,441],[137,140,142,181,263,431,441],[137,140,142,181,263,432,433,434,435,436,437,438,439,440],[137,140,142,181,263,432,436,437,440,441,444],[137,140,142,181,263,432,433,434,435,436,437,438,439,440,441,442,444,445],[137,140,142,181,263,431,432,433,434,435,436,437,438,439,440],[92,137,140,142,181,211,263],[92,137,140,142,181,211,217,263],[92,137,140,142,181,211,212,213,218,219,220,263],[137,140,142,146,181,210,211,212,221,222,233,241,263],[137,140,142,150,181,211,215,263],[137,140,142,181,211,214,263],[137,140,142,181,210,211,216,221,263],[137,140,142,181,210,211,214,215,217,221,240,263],[92,137,140,142,150,181,210,211,214,216,221,263],[137,140,142,181,210,211,216,221,234,235,236,237,238,239,263],[92,137,140,142,181,211,215,216,263],[137,140,142,181,210,211,216,236,263],[137,140,142,181,210,211,216,217,221,236,263],[137,140,142,150,181,210,211,216,221,240,263],[137,140,142,181,211,216,235,263],[137,140,142,146,181,211,215,242,263],[137,140,142,181,226,227,228,229,263],[137,140,142,181,223,224,225,230,231,232,263],[92,137,140,142,181,214,263],[92,137,140,142,181,210,214,263],[49,92,137,140,142,181,263],[92,137,140,142,181,192,263],[92,137,140,142,146,181,182,185,186,187,193,263],[137,139,140,142,181,185,194,263],[49,92,137,140,142,263],[137,140,142,181,188,189,190,191,263],[137,140,142,181,188,191,263],[137,140,142,181,188,189,190,263],[137,140,142,181,188,263],[137,140,142,181,251,252,254,255,257,258,259,260,261,263],[137,140,142,181,256,263],[137,140,142,146,181,255,260,263],[92,137,140,142,146,181,253,254,263],[49,137,139,140,142,146,181,263],[137,140,142,150,151,181,263],[137,140,142,151,152,153,154,181,263],[137,140,142,152,181,263],[137,140,142,155,181,263],[137,140,142,181,263,313],[137,140,142,181,263,315],[49,92,137,140,181,263],[137,140,142,181,263,356],[137,140,142,181,263,353,354,355,356,357,360,361,362,363,364,365,366,367,368,369,370],[137,140,142,181,263,352],[137,140,142,181,263,359],[137,140,142,181,263,353,354,355],[137,140,142,181,263,353,354],[137,140,142,181,263,356,357,359],[137,140,142,181,263,354],[137,140,142,181,263,371],[97,137,140,142,181,263,449],[137,140,142,181,263,1067,1068,1069,1070,1071],[137,140,142,181,263,1067,1069],[61,92,137,140,142,181,263],[137,140,142,181,263,1074],[58,61,92,137,140,142,181,263,1076,1077],[59,92,137,140,142,181,263],[137,140,142,181,263,1080],[137,140,142,181,263,1082],[137,140,142,181,263,1083],[137,140,142,181,263,359,1089],[137,140,142,181,263,1093,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105],[137,140,142,181,263,1093,1094,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105],[137,140,142,181,263,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105],[137,140,142,181,263,1093,1094,1095,1097,1098,1099,1100,1101,1102,1103,1104,1105],[137,140,142,181,263,1093,1094,1095,1096,1098,1099,1100,1101,1102,1103,1104,1105],[137,140,142,181,263,1093,1094,1095,1096,1097,1099,1100,1101,1102,1103,1104,1105],[137,140,142,181,263,1093,1094,1095,1096,1097,1098,1100,1101,1102,1103,1104,1105],[137,140,142,181,263,1093,1094,1095,1096,1097,1098,1099,1101,1102,1103,1104,1105],[137,140,142,181,263,1093,1094,1095,1096,1097,1098,1099,1100,1102,1103,1104,1105],[137,140,142,181,263,1093,1094,1095,1096,1097,1098,1099,1100,1101,1103,1104,1105],[137,140,142,181,263,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1104,1105],[137,140,142,181,263,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1105],[137,140,142,181,263,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104],[137,140,142,181,263,1111],[137,140,142,181,263,1107,1108,1109,1110],[90,137,140,142,181,263],[48,137,140,142,181,263],[89,90,137,140,142,181,263],[49,137,140,142,181,263],[50,58,59,66,75,137,140,142,181,263],[50,51,58,66,137,140,142,181,263],[82,137,140,142,181,263],[53,54,59,67,137,140,142,181,263],[54,75,137,140,142,181,263],[55,56,58,66,137,140,142,181,263],[56,137,140,142,181,263],[57,58,137,140,142,181,263],[58,137,140,142,181,263],[58,59,60,75,81,137,140,142,181,263],[59,60,137,140,142,181,263],[61,66,75,81,137,140,142,181,263],[58,59,61,62,66,75,78,81,137,140,142,181,263],[61,63,78,81,137,140,142,181,263],[91,137,140,142,181,263],[58,64,137,140,142,181,263],[65,81,137,140,142,181,263],[56,58,66,75,137,140,142,181,263],[67,137,140,142,181,263],[68,137,140,142,181,263],[48,69,137,140,142,181,263],[80,137,140,142,181,263],[71,137,140,142,181,263],[72,137,140,142,181,263],[58,73,137,140,142,181,263],[73,74,82,84,137,140,142,181,263],[58,75,137,140,142,181,263],[76,137,140,142,181,263],[77,137,140,142,181,263],[66,78,137,140,142,181,263],[79,137,140,142,181,263],[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,137,140,142,181,263],[66,80,137,140,142,181,263],[72,81,137,140,142,181,263],[75,83,137,140,142,181,263],[84,137,140,142,181,263],[88,137,140,142,181,263],[58,60,75,81,84,85,137,140,142,181,263],[75,86,137,140,142,181,263],[93,94,95,96,137,140,142,181,263],[58,61,63,66,75,78,81,86,92,137,140,142,181,263],[137,140,142,181,263,1118],[137,140,142,181,242,263,279],[137,140,142,181,242,263],[137,140,142,181,263,379,380,381,382,384,385],[137,140,142,181,242,263,379,380,381,383],[137,140,142,146,181,242,263],[137,140,142,181,242,263,379,380,381],[137,140,142,173,181,263],[137,140,142,173,174,175,176,177,181,263],[137,140,142,162,163,164,165,166,167,168,169,170,171,172,181,263],[137,140,142,181,263,1085,1086],[137,140,142,181,263,1085,1086,1087,1088],[115,137,140,142,181,263],[100,101,137,140,142,181,263],[100,137,140,142,181,263],[81,92,97,137,140,142,181,263],[97,107,137,140,142,181,263],[97,106,137,140,142,181,263],[109,113,137,140,142,181,263],[97,110,114,137,140,142,181,263],[104,137,140,142,181,263],[61,92,114,135,137,140,142,181,263],[100,101,102,137,140,142,181,263],[103,137,140,142,181,263],[97,99,114,135,137,140,142,181,263],[61,72,81,92,101,103,104,115,116,122,123,124,125,126,127,128,129,132,137,140,142,181,263],[61,81,92,133,137,140,142,181,263],[61,72,81,92,137,140,142,181,263],[61,72,92,101,124,137,140,142,181,263],[72,81,92,97,104,108,110,111,112,114,137,140,142,181,263],[117,137,140,142,181,263],[117,118,119,120,121,137,140,142,181,263],[103,130,131,137,140,142,181,263],[72,92,137,140,142,181,263],[61,72,81,92,97,99,104,105,113,135,137,140,142,181,263],[137,140,142,181,263,1030],[140,142,181,263],[137,140,142,181,263,951],[110,137,140,142,181,263],[61,72,92,97,98,104,114,115,134,137,140,142,181,263,948],[137,140,142,181,263,358],[97,137,140,142,181,263,976],[97,137,140,142,181,263,977,978],[137,140,142,181,263,976,977,979],[97,137,140,142,181,263,983],[97,137,140,142,181,263,971],[97,137,140,142,181,263,972,973,974,975,984],[97,137,140,142,181,263,971,972],[97,137,140,142,181,263,981,982],[97,137,140,142,181,263,980,981],[97,137,140,142,181,263,980],[97,135,137,140,142,181,263,948],[137,140,142,181,263,395,397,398,399,400,401],[137,140,142,181,263,395,396],[137,140,142,181,263,397],[137,140,142,181,263,396,397],[137,140,142,181,263,395,397],[137,140,142,181,263,448],[97,98,135,137,140,142,181,263],[137,140,142,181,263,406,410,412,413,414,415],[137,140,142,181,263,410,412,414,415],[137,140,142,181,263,406,414],[137,140,142,181,263,406,410,412,414,415],[137,140,142,181,263,406,408,409,410,412,413,414,416,417,418,419,420,421,422,423,424,425],[137,140,142,181,263,407,410,412,414],[137,140,142,181,263,414],[137,140,142,181,263,412],[137,140,142,181,263,406,407,409,410,411,414],[137,140,142,181,263,415],[137,140,142,181,263,412,413],[137,140,142,181,263,406,408,412,414],[137,140,142,181,263,406],[137,140,142,160,181,263],[137,140,142,181,263,331,338,376,953,954,955,1023,1029,1031,1032],[135,137,140,142,181,263],[97,137,140,142,159,181,263,328,331,964,966,968,987,1001,1011,1035],[97,137,140,142,181,263,331,337,374,375,452,685,687,952,958,960,961,963,987,992,1016,1022],[97,137,140,142,180,181,197,263,292,311,312,328,453,454,686],[97,137,140,142,145,159,181,263,281,288,1018],[97,137,140,142,181,263,312,1040],[97,137,140,142,145,159,181,197,263,288,312,328,332,426,430,990,994,1038,1052],[97,137,140,142,145,159,181,197,263,288,312,328,331,332,388,389,426,429,990,1008,1018,1038,1052],[97,137,140,142,145,159,181,263,288,312,332,426,430,1038,1052],[97,137,140,142,145,156,159,180,181,263,294,328,331,454,686],[97,137,140,142,145,159,181,197,263,288,312,328,331,332,390,426,429,990,1038,1052],[97,137,140,142,145,159,181,185,197,243,263,285,288,312,328,331,332,403,427,428,430,959,990,1008,1038,1052],[97,137,140,142,145,159,181,197,263,288,312,328,331,332,378,426,427,429,430,990,1008,1038,1052],[97,137,140,142,145,159,181,197,263,288,312,328,331,332,388,391,426,429,1008,1018,1038,1052],[97,137,140,142,145,159,181,197,263,288,312,328,331,332,392,426,429,990,1008,1038,1052],[97,137,140,142,145,159,181,197,263,288,312,328,331,332,393,426,429,990,1008,1038,1052],[97,137,140,142,145,159,181,185,243,263,285,288,312,328,331,332,403,427,430,959,990,1008,1038,1052],[97,137,140,142,145,159,181,197,263,288,312,328,331,332,388,394,426,429,990,1008,1018,1038,1052],[97,137,140,142,145,159,181,263,957],[97,137,140,142,181,263,331,337,685,952],[97,137,140,142,181,263,965],[97,137,140,142,145,156,159,180,181,197,206,243,263,312,328,329,331,332,337,426,429,454,685,952,987,990,994,995,1018,1039,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051],[97,137,140,142,181,263,1011,1013],[97,137,140,142,181,263,329],[97,137,140,142,181,263,326,328,329,337,453,685,952],[97,137,140,142,181,263,326,328,1056],[97,137,140,142,146,149,156,159,180,181,185,243,263,285,288,300,328,329,337,426,429,454,685,952,990],[137,140,142,145,148,156,159,181,194,197,207,263],[137,140,142,145,156,159,181,263,286,324,326,350],[137,140,142,161,181,263,309],[137,140,142,161,178,181,263],[137,140,142,161,180,181,263,287,288,310],[137,140,142,145,155,156,159,161,178,181,185,245,246,263,287,288,304,317,318,319,320,324,326,327],[97,137,140,142,181,263,373,1034],[137,140,142,181,263,372],[137,140,142,181,263,326],[137,140,142,181,263,288],[137,140,142,157,181,185,242,263],[137,140,142,155,181,242,263,324,386,388],[137,140,142,155,181,242,263,388],[137,140,142,155,181,185,197,242,263,288,324,386,388],[137,140,142,155,181,242,263,386,388],[137,140,142,181,242,263,279,323,386,387],[137,140,142,181,263,288,402,403],[137,140,142,181,263,402,403],[137,140,142,181,263,402],[137,140,142,181,185,263],[137,140,142,155,181,185,195,263,324,427],[137,140,142,146,181,263,288],[137,140,142,147,181,263],[137,140,142,143,181,263,323],[137,140,142,146,158,181,263],[137,140,142,159,181,242,243,263],[137,140,142,155,181,185,197,243,263,288,312,324,428,429],[137,140,142,179,181,263],[137,140,142,180,181,194,263],[137,140,142,181,263,286],[137,140,142,145,159,181,185,207,243,263,285,287],[137,140,142,145,156,159,181,185,263,288],[137,140,142,181,185,242,243,263,288,324,402,403,405,426],[137,140,142,181,263,314,316],[137,140,142,184,185,209,263,1120],[137,142,184,185,209,263,1120],[142,181,263],[145,156,159],[142,145,156,159,181,196,263],[142,145,156,159,181,263,304],[142,156,159,181,263],[142,155,159,181,263],[142,145,156,159,181,263],[142,146,156,159,181,263],[97],[159,185,304],[159],[145,159],[97,941,943],[97,145,159],[145,159,1019],[142,145,159,181,263,285],[142,159,181,263],[309],[145,159,309],[159,375],[288],[97,145,159,196],[145,159,288,312],[185,288],[145,159,185],[145,159,185,288,326,330],[155],[142,146,181,263],[142,145,181,263,304],[304],[142,181,263,304],[142,145,147,149,155,181,263],[142,145,147,181,263],[142,146,159,181,263],[142,145,149,159,181,263,324],[142,149,159,181,263],[142,145,146,159,181,185,263],[142,181,196,263],[142,146,149,159,181,263],[142,155,181,263],[135,136,137,140,142,184,185,209,263,1120],[137,140,142,184,185,209,263,1067,1120],[137,140,142,146,184,185,209,263,269,1120],[92,137,140,142,146,184,185,209,255,262,263,264,265,266,267,268,269,271,272,1120],[92,137,140,142,184,185,209,263,1120],[92,137,140,142,146,184,185,209,263,1120],[137,140,142,184,185,209,248,249,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,1120],[137,140,142,146,184,185,209,255,263,264,271,279,1120],[92,137,139,140,142,146,184,185,209,263,264,265,270,1120],[137,140,142,146,184,185,209,255,262,263,264,266,267,269,270,271,279,1120],[137,140,142,146,184,185,209,263,264,266,269,271,1120],[137,140,142,146,184,185,209,263,264,269,272,279,1120],[137,140,142,146,184,185,209,263,279,1120],[137,140,142,146,184,185,209,263,264,265,267,268,279,1120],[137,140,142,146,184,185,209,263,264,267,271,1120],[92,137,140,142,146,184,185,209,262,263,264,267,272,1120],[49,92,137,140,142,184,185,209,1120],[137,140,142,184,185,209,263,935,1120],[97,137,140,142,184,185,209,263,1120],[97,137,140,142,184,185,209,263,936,937,938,939,940,1120],[97,137,140,142,184,185,209,263,937,941,1120],[97,137,140,142,184,185,209,263,939,941,1120],[94,137,140,142,184,185,209,263,935,1120],[137,140,142,184,185,209,263,941,942,1120],[97,137,140,142,184,185,209,263,689,1120],[97,137,140,142,184,185,209,263,689,690,691,1120],[97,137,140,142,184,185,209,263,689,691,699,1120],[97,137,140,142,184,185,209,263,689,690,1120],[137,140,142,184,185,209,263,692,693,694,695,696,697,698,700,701,702,1120],[137,140,142,184,185,209,263,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,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,1120],[137,140,142,184,185,209,263,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,930,931,932,933,1120],[137,140,142,184,185,209,263,447,1120],[137,140,142,184,185,209,263,441,443,1120],[137,140,142,184,185,209,263,431,441,442,444,445,446,1120],[137,140,142,184,185,209,263,441,1120],[137,140,142,184,185,209,263,431,441,1120],[137,140,142,184,185,209,263,432,433,434,435,436,437,438,439,440,1120],[137,140,142,184,185,209,263,432,436,437,440,441,444,1120],[137,140,142,184,185,209,263,432,433,434,435,436,437,438,439,440,441,442,444,445,1120],[137,140,142,184,185,209,263,431,432,433,434,435,436,437,438,439,440,1120],[49,92,137,140,142,184,185,263,1120],[92,137,140,142,184,185,192,209,263,1120],[92,137,140,142,146,182,184,185,186,187,193,209,263,1120],[137,139,140,142,184,185,194,209,263,1120],[49,92,137,140,142,184,185,209,263,1120],[137,140,142,184,185,188,189,190,191,209,263,1120],[137,140,142,184,185,188,191,209,263,1120],[137,140,142,184,185,188,189,190,209,263,1120],[137,140,142,184,185,188,209,263,1120],[137,140,142,184,185,209,251,252,254,255,257,258,259,260,261,263,1120],[137,140,142,184,185,209,256,263,1120],[137,140,142,146,184,185,209,263,1120],[137,140,142,146,184,185,209,255,260,263,1120],[92,137,140,142,146,184,185,209,253,254,263,1120],[137,140,142,150,151,184,185,209,263,1120],[137,140,142,151,152,153,154,184,185,209,263,1120],[137,140,142,152,184,185,209,263,1120],[137,140,142,155,184,185,209,263,1120],[137,140,142,184,185,209,263,313,1120],[137,140,142,184,185,209,263,315,1120],[49,92,137,140,184,185,209,263,1120],[137,140,142,184,185,209,263,356,1120],[137,140,142,184,185,209,263,353,354,355,356,357,360,361,362,363,364,365,366,367,368,369,370,1120],[137,140,142,184,185,209,263,352,1120],[137,140,142,184,185,209,263,359,1120],[137,140,142,184,185,209,263,353,354,355,1120],[137,140,142,184,185,209,263,353,354,1120],[137,140,142,184,185,209,263,356,357,359,1120],[137,140,142,184,185,209,263,354,1120],[137,140,142,184,185,209,263,371,1120],[97,137,140,142,184,185,209,263,449,1120],[137,140,142,184,185,209,263,1067,1068,1069,1070,1071,1120],[137,140,142,184,185,209,263,1067,1069,1120],[61,92,137,140,142,184,185,209,263,1120],[137,140,142,184,185,209,263,1074,1120],[58,61,92,137,140,142,184,185,209,263,1076,1077,1120],[59,92,137,140,142,184,185,209,263,1120],[137,140,142,184,185,209,263,1080,1120],[137,140,142,184,185,209,263,1082,1120],[137,140,142,184,185,209,263,1083,1120],[137,140,142,184,185,209,263,359,1089,1120],[137,140,142,184,185,209,263,1093,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1120],[137,140,142,184,185,209,263,1093,1094,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1120],[137,140,142,184,185,209,263,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1120],[137,140,142,184,185,209,263,1093,1094,1095,1097,1098,1099,1100,1101,1102,1103,1104,1105,1120],[137,140,142,184,185,209,263,1093,1094,1095,1096,1098,1099,1100,1101,1102,1103,1104,1105,1120],[137,140,142,184,185,209,263,1093,1094,1095,1096,1097,1099,1100,1101,1102,1103,1104,1105,1120],[137,140,142,184,185,209,263,1093,1094,1095,1096,1097,1098,1100,1101,1102,1103,1104,1105,1120],[137,140,142,184,185,209,263,1093,1094,1095,1096,1097,1098,1099,1101,1102,1103,1104,1105,1120],[137,140,142,184,185,209,263,1093,1094,1095,1096,1097,1098,1099,1100,1102,1103,1104,1105,1120],[137,140,142,184,185,209,263,1093,1094,1095,1096,1097,1098,1099,1100,1101,1103,1104,1105,1120],[137,140,142,184,185,209,263,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1104,1105,1120],[137,140,142,184,185,209,263,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1105,1120],[137,140,142,184,185,209,263,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,1103,1104,1120],[137,140,142,184,185,209,263,1111,1120],[137,140,142,184,185,209,263,1107,1108,1109,1110,1120],[90,137,140,142,184,185,209,263,1120],[48,137,140,142,184,185,209,263,1120],[89,90,137,140,142,184,185,209,263,1120],[49,137,140,142,184,185,209,263,1120],[50,58,59,66,75,137,140,142,184,185,209,263,1120],[50,51,58,66,137,140,142,184,185,209,263,1120],[82,137,140,142,184,185,209,263,1120],[53,54,59,67,137,140,142,184,185,209,263,1120],[54,75,137,140,142,184,185,209,263,1120],[55,56,58,66,137,140,142,184,185,209,263,1120],[56,137,140,142,184,185,209,263,1120],[57,58,137,140,142,184,185,209,263,1120],[58,137,140,142,184,185,209,263,1120],[58,59,60,75,81,137,140,142,184,185,209,263,1120],[59,60,137,140,142,184,185,209,263,1120],[61,66,75,81,137,140,142,184,185,209,263,1120],[58,59,61,62,66,75,78,81,137,140,142,184,185,209,263,1120],[61,63,78,81,137,140,142,184,185,209,263,1120],[91,137,140,142,184,185,209,263,1120],[58,64,137,140,142,184,185,209,263,1120],[65,81,137,140,142,184,185,209,263,1120],[56,58,66,75,137,140,142,184,185,209,263,1120],[67,137,140,142,184,185,209,263,1120],[68,137,140,142,184,185,209,263,1120],[48,69,137,140,142,184,185,209,263,1120],[80,137,140,142,184,185,209,263,1120],[71,137,140,142,184,185,209,263,1120],[72,137,140,142,184,185,209,263,1120],[58,73,137,140,142,184,185,209,263,1120],[73,74,82,84,137,140,142,184,185,209,263,1120],[58,75,137,140,142,184,185,209,263,1120],[76,137,140,142,184,185,209,263,1120],[77,137,140,142,184,185,209,263,1120],[66,78,137,140,142,184,185,209,263,1120],[79,137,140,142,184,185,209,263,1120],[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,137,140,142,184,185,209,263,1120],[66,80,137,140,142,184,185,209,263,1120],[72,81,137,140,142,184,185,209,263,1120],[75,83,137,140,142,184,185,209,263,1120],[84,137,140,142,184,185,209,263,1120],[88,137,140,142,184,185,209,263,1120],[58,60,75,81,84,85,137,140,142,184,185,209,263,1120],[75,86,137,140,142,184,185,209,263,1120],[93,94,95,96,137,140,142,184,185,209,263,1120],[58,61,63,66,75,78,81,86,92,137,140,142,184,185,209,263,1120],[137,140,142,184,185,209,263,1118,1120],[137,140,142,173,184,185,209,263,1120],[137,140,142,173,174,175,176,177,184,185,209,263,1120],[137,140,142,162,163,164,165,166,167,168,169,170,171,172,184,185,209,263,1120],[137,140,142,184,185,209,263,1085,1086,1120],[137,140,142,184,185,209,263,1085,1086,1087,1088,1120],[115,137,140,142,184,185,209,263,1120],[100,101,137,140,142,184,185,209,263,1120],[100,137,140,142,184,185,209,263,1120],[81,92,97,137,140,142,184,185,209,263,1120],[97,107,137,140,142,184,185,209,263,1120],[97,106,137,140,142,184,185,209,263,1120],[109,113,137,140,142,184,185,209,263,1120],[97,110,114,137,140,142,184,185,209,263,1120],[104,137,140,142,184,185,209,263,1120],[61,92,114,135,137,140,142,184,185,209,263,1120],[100,101,102,137,140,142,184,185,209,263,1120],[103,137,140,142,184,185,209,263,1120],[97,99,114,135,137,140,142,184,185,209,263,1120],[61,72,81,92,101,103,104,115,116,122,123,124,125,126,127,128,129,132,137,140,142,184,185,209,263,1120],[61,81,92,133,137,140,142,184,185,209,263,1120],[61,72,81,92,137,140,142,184,185,209,263,1120],[61,72,92,101,124,137,140,142,184,185,209,263,1120],[72,81,92,97,104,108,110,111,112,114,137,140,142,184,185,209,263,1120],[117,137,140,142,184,185,209,263,1120],[117,118,119,120,121,137,140,142,184,185,209,263,1120],[103,130,131,137,140,142,184,185,209,263,1120],[72,92,137,140,142,184,185,209,263,1120],[61,72,81,92,97,99,104,105,113,135,137,140,142,184,185,209,263,1120],[137,140,142,184,185,209,263,1030,1120],[140,142,184,185,209,263,1120],[137,140,142,184,185,209,263,951,1120],[110,137,140,142,184,185,209,263,1120],[61,72,92,97,98,104,114,115,134,137,140,142,184,185,209,263,948,1120],[137,140,142,184,185,209,263,358,1120],[97,137,140,142,184,185,209,263,976,1120],[97,137,140,142,184,185,209,263,977,978,1120],[137,140,142,184,185,209,263,976,977,979,1120],[97,137,140,142,184,185,209,263,983,1120],[97,137,140,142,184,185,209,263,971,1120],[97,137,140,142,184,185,209,263,972,973,974,975,984,1120],[97,137,140,142,184,185,209,263,971,972,1120],[97,137,140,142,184,185,209,263,981,982,1120],[97,137,140,142,184,185,209,263,980,981,1120],[97,137,140,142,184,185,209,263,980,1120],[97,135,137,140,142,184,185,209,263,948,1120],[137,140,142,184,185,209,263,395,397,398,399,400,401,1120],[137,140,142,184,185,209,263,395,396,1120],[137,140,142,184,185,209,263,397,1120],[137,140,142,184,185,209,263,396,397,1120],[137,140,142,184,185,209,263,395,397,1120],[137,140,142,184,185,209,263,448,1120],[97,98,135,137,140,142,184,185,209,263,1120],[137,140,142,184,185,209,263,406,410,412,413,414,415,1120],[137,140,142,184,185,209,263,410,412,414,415,1120],[137,140,142,184,185,209,263,406,414,1120],[137,140,142,184,185,209,263,406,410,412,414,415,1120],[137,140,142,184,185,209,263,406,408,409,410,412,413,414,416,417,418,419,420,421,422,423,424,425,1120],[137,140,142,184,185,209,263,407,410,412,414,1120],[137,140,142,184,185,209,263,414,1120],[137,140,142,184,185,209,263,412,1120],[137,140,142,184,185,209,263,406,407,409,410,411,414,1120],[137,140,142,184,185,209,263,415,1120],[137,140,142,184,185,209,263,412,413,1120],[137,140,142,184,185,209,263,406,408,412,414,1120],[137,140,142,184,185,209,263,406,1120],[137,140,142,160,184,185,209,263,1120],[135],[312],[145,159,288],[97,312],[326],[161,309],[161],[142,161,181,263,287,288,310],[142,145,155,159,161,181,185,263,288,304,320,324],[356,372],[157,185,242],[142,155,181,263,324],[142,181,242,263,279,323,386],[242],[142,181,263,288,402],[142,181,263,402],[142,146,181,263,288],[147],[142,181,263,323],[142,155,184,209,263,288,312,324],[142,145,159,181,185,263],[142,145,156,159,181,185,263,288],[142,181,185,263,288,324,414,421,426],[314,316]],"referencedMap":[[139,1],[140,2],[143,1],[199,3],[201,4],[308,5],[206,6],[291,7],[292,8],[294,9],[296,10],[298,11],[300,12],[302,13],[303,14],[452,15],[454,16],[688,17],[1014,18],[1015,19],[1016,20],[944,21],[945,22],[1017,23],[311,1],[946,24],[947,1],[967,1],[989,25],[990,26],[1018,27],[988,28],[994,26],[1019,29],[1020,30],[1021,31],[1022,32],[208,1],[247,33],[280,34],[281,1],[282,35],[283,36],[284,37],[285,38],[949,39],[950,39],[453,40],[996,41],[997,42],[998,43],[1001,44],[1000,45],[309,46],[999,47],[686,48],[953,49],[954,50],[955,1],[956,51],[957,40],[964,52],[966,53],[958,54],[963,55],[968,56],[969,57],[970,58],[986,59],[965,40],[987,60],[451,61],[1003,62],[1004,63],[1005,64],[1011,65],[1006,66],[1010,67],[1007,68],[1013,69],[1009,70],[310,1],[991,71],[962,72],[992,73],[961,1],[332,74],[334,75],[1023,76],[1024,40],[1025,40],[333,77],[1027,78],[1028,40],[335,40],[687,79],[960,80],[337,81],[331,82],[336,83],[338,84],[1029,85],[1061,1],[159,86],[246,87],[304,88],[319,89],[305,90],[306,91],[307,92],[145,1],[156,93],[144,1],[244,94],[339,95],[340,96],[320,1],[341,1],[196,97],[326,98],[149,1],[197,99],[323,1],[330,100],[203,101],[198,102],[200,101],[342,95],[343,95],[344,95],[202,101],[299,103],[290,104],[289,105],[345,95],[293,95],[295,102],[297,102],[204,101],[301,102],[346,102],[347,102],[205,102],[348,95],[138,106],[1063,107],[1069,108],[1067,1],[268,109],[274,110],[275,111],[264,112],[248,1],[279,113],[276,114],[271,115],[249,1],[272,116],[267,117],[270,118],[273,119],[269,120],[266,121],[277,1],[265,1],[278,122],[263,123],[936,124],[939,40],[941,125],[938,126],[940,127],[937,128],[942,126],[943,129],[935,1],[691,130],[692,131],[693,130],[694,130],[699,130],[695,130],[696,130],[697,130],[698,130],[700,132],[701,132],[702,133],[703,134],[689,40],[690,130],[455,40],[456,40],[457,40],[458,40],[459,40],[460,40],[461,40],[462,40],[463,40],[464,40],[465,40],[466,40],[467,40],[468,40],[469,40],[475,40],[470,40],[471,40],[472,40],[473,40],[474,40],[476,40],[477,40],[478,40],[479,40],[480,40],[481,40],[483,40],[484,40],[482,40],[485,40],[486,40],[487,40],[488,40],[489,40],[490,40],[491,40],[492,40],[493,40],[494,40],[495,40],[496,40],[497,40],[498,40],[499,40],[500,40],[501,40],[502,40],[503,40],[504,40],[505,40],[506,40],[507,40],[508,40],[509,40],[511,40],[510,40],[512,40],[513,40],[515,40],[514,40],[516,40],[517,40],[518,40],[519,40],[520,40],[522,40],[521,40],[523,40],[524,40],[525,40],[526,40],[527,40],[528,40],[529,40],[530,40],[531,40],[532,40],[533,40],[534,40],[535,40],[536,40],[541,40],[537,40],[538,40],[539,40],[540,40],[542,40],[543,40],[544,40],[545,40],[546,40],[547,40],[548,40],[549,40],[550,40],[551,40],[553,40],[552,40],[554,40],[555,40],[556,40],[557,40],[558,40],[559,40],[560,40],[561,40],[564,40],[562,40],[563,40],[565,40],[566,40],[567,40],[568,40],[569,40],[570,40],[571,40],[572,40],[574,40],[573,40],[685,135],[575,40],[576,40],[577,40],[578,40],[579,40],[580,40],[581,40],[582,40],[583,40],[584,40],[585,40],[587,40],[586,40],[588,40],[589,40],[590,40],[591,40],[592,40],[593,40],[594,40],[595,40],[597,40],[596,40],[598,40],[599,40],[600,40],[601,40],[602,40],[603,40],[604,40],[605,40],[606,40],[610,40],[607,40],[608,40],[609,40],[611,40],[612,40],[613,40],[615,40],[614,40],[616,40],[617,40],[618,40],[619,40],[620,40],[621,40],[622,40],[623,40],[624,40],[625,40],[626,40],[627,40],[628,40],[629,40],[630,40],[631,40],[632,40],[633,40],[634,40],[635,40],[636,40],[637,40],[638,40],[639,40],[640,40],[641,40],[642,40],[643,40],[644,40],[645,40],[646,40],[647,40],[648,40],[649,40],[650,40],[651,40],[652,40],[653,40],[654,40],[655,40],[656,40],[657,40],[658,40],[659,40],[660,40],[661,40],[662,40],[663,40],[664,40],[665,40],[666,40],[667,40],[668,40],[670,40],[669,40],[671,40],[672,40],[673,40],[674,40],[675,40],[676,40],[677,40],[678,40],[679,40],[680,40],[681,40],[682,40],[683,40],[684,40],[704,40],[705,40],[706,40],[707,40],[708,40],[709,40],[710,40],[711,40],[712,40],[713,40],[714,40],[715,40],[716,40],[717,40],[718,40],[724,40],[719,40],[720,40],[721,40],[722,40],[723,40],[725,40],[726,40],[727,40],[728,40],[729,40],[730,40],[732,40],[733,40],[731,40],[734,40],[735,40],[736,40],[737,40],[738,40],[739,40],[740,40],[741,40],[742,40],[743,40],[744,40],[745,40],[746,40],[747,40],[748,40],[749,40],[750,40],[751,40],[752,40],[753,40],[754,40],[755,40],[756,40],[757,40],[758,40],[760,40],[759,40],[761,40],[762,40],[764,40],[763,40],[765,40],[766,40],[767,40],[768,40],[769,40],[771,40],[770,40],[772,40],[773,40],[774,40],[775,40],[776,40],[777,40],[778,40],[779,40],[780,40],[781,40],[782,40],[783,40],[784,40],[785,40],[790,40],[786,40],[787,40],[788,40],[789,40],[791,40],[792,40],[793,40],[794,40],[795,40],[796,40],[797,40],[798,40],[799,40],[800,40],[802,40],[801,40],[803,40],[804,40],[805,40],[806,40],[807,40],[808,40],[809,40],[810,40],[813,40],[811,40],[812,40],[814,40],[815,40],[816,40],[817,40],[818,40],[819,40],[820,40],[821,40],[823,40],[822,40],[934,136],[824,40],[825,40],[826,40],[827,40],[828,40],[829,40],[830,40],[831,40],[832,40],[833,40],[834,40],[836,40],[835,40],[837,40],[838,40],[839,40],[840,40],[841,40],[842,40],[843,40],[844,40],[846,40],[845,40],[847,40],[848,40],[849,40],[850,40],[851,40],[852,40],[853,40],[854,40],[855,40],[859,40],[856,40],[857,40],[858,40],[860,40],[861,40],[862,40],[864,40],[863,40],[865,40],[866,40],[867,40],[868,40],[869,40],[870,40],[871,40],[872,40],[873,40],[874,40],[875,40],[876,40],[877,40],[878,40],[879,40],[880,40],[881,40],[882,40],[883,40],[884,40],[885,40],[886,40],[887,40],[888,40],[889,40],[890,40],[891,40],[892,40],[893,40],[894,40],[895,40],[896,40],[897,40],[898,40],[899,40],[900,40],[901,40],[902,40],[903,40],[904,40],[905,40],[906,40],[907,40],[908,40],[909,40],[910,40],[911,40],[912,40],[913,40],[914,40],[915,40],[916,40],[917,40],[919,40],[918,40],[920,40],[921,40],[922,40],[923,40],[924,40],[925,40],[926,40],[927,40],[928,40],[929,40],[930,40],[931,40],[932,40],[933,40],[105,1],[448,137],[444,138],[431,1],[447,139],[440,140],[438,141],[437,141],[436,140],[433,141],[434,140],[442,142],[435,141],[432,140],[439,141],[445,143],[446,144],[441,145],[443,141],[213,146],[220,146],[218,147],[221,148],[212,146],[219,146],[222,1],[211,111],[242,149],[214,150],[215,151],[217,152],[241,153],[238,154],[240,155],[235,156],[237,157],[239,158],[234,159],[236,160],[216,161],[210,1],[229,111],[228,111],[226,111],[230,162],[227,1],[232,1],[233,163],[225,164],[224,165],[223,1],[231,1],[209,166],[193,167],[194,168],[182,1],[187,1],[186,169],[181,170],[192,171],[188,1],[190,172],[191,173],[189,174],[252,1],[258,1],[262,175],[257,176],[256,88],[261,177],[260,1],[255,178],[254,112],[253,112],[251,1],[259,1],[250,166],[185,179],[184,166],[183,1],[152,180],[151,1],[155,181],[153,182],[154,182],[313,183],[314,184],[315,183],[316,185],[142,186],[369,1],[366,1],[365,1],[357,187],[371,188],[353,189],[367,190],[356,191],[355,192],[368,1],[360,193],[370,1],[362,194],[364,194],[363,194],[354,1],[361,1],[372,195],[450,196],[352,1],[1072,197],[1068,108],[1070,198],[1071,108],[146,111],[1073,199],[1075,200],[1078,201],[1079,202],[1081,203],[1082,1],[1083,204],[1084,205],[1090,206],[1091,1],[1092,1],[1094,207],[1095,208],[1093,209],[1096,210],[1097,211],[1098,212],[1099,213],[1100,214],[1101,215],[1102,216],[1103,217],[1104,218],[1105,219],[1106,203],[1108,1],[1107,1],[1110,220],[1111,221],[1109,220],[1074,1],[90,222],[48,223],[91,224],[49,225],[50,226],[51,227],[52,228],[53,229],[54,230],[55,231],[56,232],[57,233],[58,234],[59,235],[60,236],[47,1],[87,1],[61,237],[62,238],[63,239],[92,240],[64,241],[65,242],[66,243],[67,244],[68,245],[69,246],[70,247],[71,248],[72,249],[73,250],[74,251],[75,252],[76,253],[77,254],[78,255],[79,256],[89,257],[80,258],[81,259],[82,228],[83,260],[84,261],[88,262],[85,263],[86,264],[1112,1],[1113,1],[1114,1],[95,1],[1077,1],[1076,1],[93,1],[97,265],[1115,1],[96,1],[1116,1],[1080,1],[1117,266],[1118,1],[1119,267],[379,1],[381,268],[380,1],[385,269],[386,270],[384,271],[383,272],[382,273],[286,1],[157,1],[147,112],[141,1],[94,1],[150,1],[172,1],[169,274],[171,274],[170,274],[168,274],[178,275],[173,276],[177,1],[174,1],[176,1],[175,1],[164,274],[165,274],[166,274],[162,1],[163,1],[167,274],[1085,1],[1087,277],[1089,278],[1088,277],[1086,1],[158,1],[1032,40],[1062,1],[116,279],[106,280],[129,281],[126,281],[951,282],[108,283],[107,284],[110,285],[109,286],[100,1],[101,287],[115,288],[103,289],[104,290],[127,1],[99,1],[102,1],[123,291],[133,292],[134,293],[124,294],[125,295],[128,290],[1030,40],[131,1],[111,1],[113,296],[118,297],[122,298],[121,1],[132,299],[112,300],[130,300],[119,297],[117,1],[120,1],[114,301],[1031,302],[137,303],[952,304],[329,305],[136,111],[135,306],[359,307],[358,1],[977,308],[979,309],[980,310],[976,40],[978,40],[984,311],[975,312],[974,40],[985,313],[971,40],[973,314],[972,312],[983,315],[982,316],[981,317],[98,318],[395,1],[402,319],[397,320],[398,321],[399,321],[400,322],[401,322],[396,323],[449,324],[948,325],[10,1],[11,1],[13,1],[12,1],[2,1],[14,1],[15,1],[16,1],[17,1],[18,1],[19,1],[20,1],[21,1],[3,1],[4,1],[25,1],[22,1],[23,1],[24,1],[26,1],[27,1],[28,1],[5,1],[29,1],[30,1],[31,1],[32,1],[6,1],[33,1],[34,1],[35,1],[36,1],[7,1],[41,1],[37,1],[38,1],[39,1],[40,1],[8,1],[45,1],[42,1],[43,1],[44,1],[1,1],[9,1],[46,1],[422,326],[417,327],[407,328],[420,329],[426,330],[413,331],[415,332],[416,327],[419,329],[421,326],[406,333],[412,334],[425,335],[418,329],[414,336],[409,337],[424,332],[423,1],[411,338],[410,1],[408,1],[161,339],[160,1],[1033,340],[349,341],[1036,342],[1037,343],[1040,344],[1038,345],[1041,346],[1045,347],[1050,348],[1043,349],[1054,350],[1046,351],[1044,352],[1042,353],[1048,354],[1049,355],[1047,356],[1039,357],[1051,358],[1053,359],[1035,360],[995,361],[1052,362],[1055,363],[1034,364],[1056,365],[1057,366],[1058,367],[1064,1],[321,1],[322,1],[350,368],[351,369],[993,370],[179,371],[1002,372],[328,373],[1065,1],[1066,1],[1059,374],[373,375],[375,1],[327,1],[376,1],[245,1],[325,1],[374,376],[377,1],[378,377],[243,378],[389,379],[390,380],[391,381],[392,382],[393,382],[394,379],[388,383],[387,269],[404,384],[405,385],[403,386],[1060,387],[428,388],[1026,389],[148,390],[324,391],[1008,1],[959,392],[429,25],[318,1],[1012,393],[207,1],[430,394],[180,395],[195,396],[287,397],[288,398],[312,399],[427,400],[317,401]],"exportedModulesMap":[[139,402],[140,403],[143,404],[199,405],[201,406],[308,407],[206,408],[291,408],[292,409],[294,405],[296,410],[298,410],[300,411],[302,410],[303,405],[454,412],[1014,413],[1017,414],[311,404],[946,415],[988,416],[1019,417],[1020,418],[1021,419],[247,420],[280,420],[281,404],[282,420],[283,420],[284,420],[285,420],[949,412],[950,412],[453,412],[997,421],[309,415],[999,422],[964,420],[958,420],[963,414],[970,415],[986,412],[965,412],[987,423],[451,412],[1004,424],[991,425],[962,414],[332,426],[334,415],[1027,427],[687,428],[960,414],[331,429],[336,420],[1029,430],[159,431],[246,410],[304,431],[319,432],[305,433],[306,432],[307,434],[145,404],[156,435],[244,436],[339,420],[340,437],[341,404],[196,420],[326,438],[197,439],[330,440],[203,404],[198,404],[200,441],[342,420],[343,420],[344,420],[202,404],[299,442],[290,443],[289,420],[345,404],[293,420],[295,404],[297,404],[204,420],[301,404],[346,404],[347,404],[205,404],[348,404],[138,444],[1069,445],[1067,402],[268,446],[274,447],[275,448],[264,449],[248,402],[279,450],[276,451],[271,452],[249,402],[272,453],[267,454],[270,455],[273,456],[269,457],[266,458],[277,402],[265,402],[278,459],[263,460],[936,461],[939,462],[941,463],[938,464],[940,465],[937,466],[942,464],[943,467],[935,402],[691,468],[692,469],[693,468],[694,468],[699,468],[695,468],[696,468],[697,468],[698,468],[700,470],[701,470],[702,471],[703,472],[689,462],[690,468],[455,462],[456,462],[457,462],[458,462],[459,462],[460,462],[461,462],[462,462],[463,462],[464,462],[465,462],[466,462],[467,462],[468,462],[469,462],[475,462],[470,462],[471,462],[472,462],[473,462],[474,462],[476,462],[477,462],[478,462],[479,462],[480,462],[481,462],[483,462],[484,462],[482,462],[485,462],[486,462],[487,462],[488,462],[489,462],[490,462],[491,462],[492,462],[493,462],[494,462],[495,462],[496,462],[497,462],[498,462],[499,462],[500,462],[501,462],[502,462],[503,462],[504,462],[505,462],[506,462],[507,462],[508,462],[509,462],[511,462],[510,462],[512,462],[513,462],[515,462],[514,462],[516,462],[517,462],[518,462],[519,462],[520,462],[522,462],[521,462],[523,462],[524,462],[525,462],[526,462],[527,462],[528,462],[529,462],[530,462],[531,462],[532,462],[533,462],[534,462],[535,462],[536,462],[541,462],[537,462],[538,462],[539,462],[540,462],[542,462],[543,462],[544,462],[545,462],[546,462],[547,462],[548,462],[549,462],[550,462],[551,462],[553,462],[552,462],[554,462],[555,462],[556,462],[557,462],[558,462],[559,462],[560,462],[561,462],[564,462],[562,462],[563,462],[565,462],[566,462],[567,462],[568,462],[569,462],[570,462],[571,462],[572,462],[574,462],[573,462],[685,473],[575,462],[576,462],[577,462],[578,462],[579,462],[580,462],[581,462],[582,462],[583,462],[584,462],[585,462],[587,462],[586,462],[588,462],[589,462],[590,462],[591,462],[592,462],[593,462],[594,462],[595,462],[597,462],[596,462],[598,462],[599,462],[600,462],[601,462],[602,462],[603,462],[604,462],[605,462],[606,462],[610,462],[607,462],[608,462],[609,462],[611,462],[612,462],[613,462],[615,462],[614,462],[616,462],[617,462],[618,462],[619,462],[620,462],[621,462],[622,462],[623,462],[624,462],[625,462],[626,462],[627,462],[628,462],[629,462],[630,462],[631,462],[632,462],[633,462],[634,462],[635,462],[636,462],[637,462],[638,462],[639,462],[640,462],[641,462],[642,462],[643,462],[644,462],[645,462],[646,462],[647,462],[648,462],[649,462],[650,462],[651,462],[652,462],[653,462],[654,462],[655,462],[656,462],[657,462],[658,462],[659,462],[660,462],[661,462],[662,462],[663,462],[664,462],[665,462],[666,462],[667,462],[668,462],[670,462],[669,462],[671,462],[672,462],[673,462],[674,462],[675,462],[676,462],[677,462],[678,462],[679,462],[680,462],[681,462],[682,462],[683,462],[684,462],[704,462],[705,462],[706,462],[707,462],[708,462],[709,462],[710,462],[711,462],[712,462],[713,462],[714,462],[715,462],[716,462],[717,462],[718,462],[724,462],[719,462],[720,462],[721,462],[722,462],[723,462],[725,462],[726,462],[727,462],[728,462],[729,462],[730,462],[732,462],[733,462],[731,462],[734,462],[735,462],[736,462],[737,462],[738,462],[739,462],[740,462],[741,462],[742,462],[743,462],[744,462],[745,462],[746,462],[747,462],[748,462],[749,462],[750,462],[751,462],[752,462],[753,462],[754,462],[755,462],[756,462],[757,462],[758,462],[760,462],[759,462],[761,462],[762,462],[764,462],[763,462],[765,462],[766,462],[767,462],[768,462],[769,462],[771,462],[770,462],[772,462],[773,462],[774,462],[775,462],[776,462],[777,462],[778,462],[779,462],[780,462],[781,462],[782,462],[783,462],[784,462],[785,462],[790,462],[786,462],[787,462],[788,462],[789,462],[791,462],[792,462],[793,462],[794,462],[795,462],[796,462],[797,462],[798,462],[799,462],[800,462],[802,462],[801,462],[803,462],[804,462],[805,462],[806,462],[807,462],[808,462],[809,462],[810,462],[813,462],[811,462],[812,462],[814,462],[815,462],[816,462],[817,462],[818,462],[819,462],[820,462],[821,462],[823,462],[822,462],[934,474],[824,462],[825,462],[826,462],[827,462],[828,462],[829,462],[830,462],[831,462],[832,462],[833,462],[834,462],[836,462],[835,462],[837,462],[838,462],[839,462],[840,462],[841,462],[842,462],[843,462],[844,462],[846,462],[845,462],[847,462],[848,462],[849,462],[850,462],[851,462],[852,462],[853,462],[854,462],[855,462],[859,462],[856,462],[857,462],[858,462],[860,462],[861,462],[862,462],[864,462],[863,462],[865,462],[866,462],[867,462],[868,462],[869,462],[870,462],[871,462],[872,462],[873,462],[874,462],[875,462],[876,462],[877,462],[878,462],[879,462],[880,462],[881,462],[882,462],[883,462],[884,462],[885,462],[886,462],[887,462],[888,462],[889,462],[890,462],[891,462],[892,462],[893,462],[894,462],[895,462],[896,462],[897,462],[898,462],[899,462],[900,462],[901,462],[902,462],[903,462],[904,462],[905,462],[906,462],[907,462],[908,462],[909,462],[910,462],[911,462],[912,462],[913,462],[914,462],[915,462],[916,462],[917,462],[919,462],[918,462],[920,462],[921,462],[922,462],[923,462],[924,462],[925,462],[926,462],[927,462],[928,462],[929,462],[930,462],[931,462],[932,462],[933,462],[105,402],[448,475],[444,476],[431,402],[447,477],[440,478],[438,479],[437,479],[436,478],[433,479],[434,478],[442,480],[435,479],[432,478],[439,479],[445,481],[446,482],[441,483],[443,479],[213,146],[220,146],[218,147],[221,148],[212,146],[219,146],[222,1],[211,111],[242,149],[214,150],[215,151],[217,152],[241,153],[238,154],[240,155],[235,156],[237,157],[239,158],[234,159],[236,160],[216,161],[210,1],[229,111],[228,111],[226,111],[230,162],[227,1],[232,1],[233,163],[225,164],[224,165],[223,1],[231,1],[209,484],[193,485],[194,486],[182,402],[187,402],[186,487],[181,488],[192,489],[188,402],[190,490],[191,491],[189,492],[252,402],[258,402],[262,493],[257,494],[256,495],[261,496],[260,402],[255,497],[254,449],[253,449],[251,402],[259,402],[250,488],[185,179],[184,166],[183,402],[152,498],[151,402],[155,499],[153,500],[154,500],[313,501],[314,502],[315,501],[316,503],[142,504],[369,402],[366,402],[365,402],[357,505],[371,506],[353,507],[367,508],[356,509],[355,510],[368,402],[360,511],[370,402],[362,512],[364,512],[363,512],[354,402],[361,402],[372,513],[450,514],[352,402],[1072,515],[1068,445],[1070,516],[1071,445],[146,448],[1073,517],[1075,518],[1078,519],[1079,520],[1081,521],[1082,402],[1083,522],[1084,523],[1090,524],[1091,402],[1092,402],[1094,525],[1095,526],[1093,527],[1096,528],[1097,529],[1098,530],[1099,531],[1100,532],[1101,533],[1102,534],[1103,535],[1104,536],[1105,537],[1106,521],[1108,402],[1107,402],[1110,538],[1111,539],[1109,538],[1074,402],[90,540],[48,541],[91,542],[49,543],[50,544],[51,545],[52,546],[53,547],[54,548],[55,549],[56,550],[57,551],[58,552],[59,553],[60,554],[47,402],[87,402],[61,555],[62,556],[63,557],[92,558],[64,559],[65,560],[66,561],[67,562],[68,563],[69,564],[70,565],[71,566],[72,567],[73,568],[74,569],[75,570],[76,571],[77,572],[78,573],[79,574],[89,575],[80,576],[81,577],[82,546],[83,578],[84,579],[88,580],[85,581],[86,582],[1112,402],[1113,402],[1114,402],[95,402],[1077,402],[1076,402],[93,402],[97,583],[1115,402],[96,402],[1116,402],[1080,402],[1117,584],[1118,402],[1119,585],[379,1],[381,268],[380,1],[385,269],[386,270],[384,271],[383,272],[382,273],[286,402],[157,402],[147,449],[141,402],[94,402],[150,402],[172,402],[169,586],[171,586],[170,586],[168,586],[178,587],[173,588],[177,402],[174,402],[176,402],[175,402],[164,586],[165,586],[166,586],[162,402],[163,402],[167,586],[1085,402],[1087,589],[1089,590],[1088,589],[1086,402],[158,402],[1032,462],[1062,402],[116,591],[106,592],[129,593],[126,593],[951,594],[108,595],[107,596],[110,597],[109,598],[100,402],[101,599],[115,600],[103,601],[104,602],[127,402],[99,402],[102,402],[123,603],[133,604],[134,605],[124,606],[125,607],[128,602],[1030,462],[131,402],[111,402],[113,608],[118,609],[122,610],[121,402],[132,611],[112,612],[130,612],[119,609],[117,402],[120,402],[114,613],[1031,614],[137,615],[952,616],[329,617],[136,448],[135,618],[359,619],[358,402],[977,620],[979,621],[980,622],[976,462],[978,462],[984,623],[975,624],[974,462],[985,625],[971,462],[973,626],[972,624],[983,627],[982,628],[981,629],[98,630],[395,402],[402,631],[397,632],[398,633],[399,633],[400,634],[401,634],[396,635],[449,636],[948,637],[10,402],[11,402],[13,402],[12,402],[2,402],[14,402],[15,402],[16,402],[17,402],[18,402],[19,402],[20,402],[21,402],[3,402],[4,402],[25,402],[22,402],[23,402],[24,402],[26,402],[27,402],[28,402],[5,402],[29,402],[30,402],[31,402],[32,402],[6,402],[33,402],[34,402],[35,402],[36,402],[7,402],[41,402],[37,402],[38,402],[39,402],[40,402],[8,402],[45,402],[42,402],[43,402],[44,402],[1,402],[9,402],[46,402],[422,638],[417,639],[407,640],[420,641],[426,642],[413,643],[415,644],[416,639],[419,641],[421,638],[406,645],[412,646],[425,647],[418,641],[414,648],[409,649],[424,644],[423,402],[411,650],[410,402],[408,402],[161,651],[160,402],[349,652],[1040,653],[1038,654],[1041,653],[1045,415],[1050,415],[1043,415],[1054,415],[1046,415],[1044,415],[1042,415],[1048,415],[1049,415],[1047,415],[1039,415],[1051,415],[1053,415],[1052,655],[1056,656],[350,410],[993,657],[179,658],[1002,659],[328,660],[373,661],[327,404],[374,656],[377,404],[378,404],[243,662],[389,663],[390,443],[391,663],[392,443],[393,443],[394,663],[388,664],[387,665],[404,666],[405,667],[403,667],[1060,404],[428,663],[1026,668],[148,669],[324,670],[959,431],[1012,414],[430,671],[195,404],[288,672],[312,673],[427,674],[317,675]],"semanticDiagnosticsPerFile":[139,140,143,199,201,308,206,291,292,294,296,298,300,302,303,452,454,688,1014,1015,1016,944,945,1017,311,946,947,967,989,990,1018,988,994,1019,1020,1021,1022,208,247,280,281,282,283,284,285,949,950,453,996,997,998,1001,[1000,[{"file":"./components/members/membersitems.tsx","start":108,"length":19,"messageText":"Cannot find module 'react-virtualized' or its corresponding type declarations.","category":1,"code":2307}]],309,999,686,953,954,955,956,957,964,966,958,963,968,969,970,986,965,987,451,1003,1004,1005,1011,1006,1010,1007,1013,1009,310,991,962,992,961,332,334,1023,1024,1025,333,1027,1028,335,687,960,337,331,336,338,1029,1061,159,246,304,319,305,306,307,145,156,144,244,339,340,320,341,196,326,149,197,323,330,203,198,200,342,343,344,202,299,290,289,345,293,295,297,204,301,346,347,205,348,138,1063,1069,1067,268,274,275,264,248,279,276,271,249,272,267,270,273,269,266,277,265,278,263,936,939,941,938,940,937,942,943,935,691,692,693,694,699,695,696,697,698,700,701,702,703,689,690,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,475,470,471,472,473,474,476,477,478,479,480,481,483,484,482,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,511,510,512,513,515,514,516,517,518,519,520,522,521,523,524,525,526,527,528,529,530,531,532,533,534,535,536,541,537,538,539,540,542,543,544,545,546,547,548,549,550,551,553,552,554,555,556,557,558,559,560,561,564,562,563,565,566,567,568,569,570,571,572,574,573,685,575,576,577,578,579,580,581,582,583,584,585,587,586,588,589,590,591,592,593,594,595,597,596,598,599,600,601,602,603,604,605,606,610,607,608,609,611,612,613,615,614,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,670,669,671,672,673,674,675,676,677,678,679,680,681,682,683,684,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,724,719,720,721,722,723,725,726,727,728,729,730,732,733,731,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,760,759,761,762,764,763,765,766,767,768,769,771,770,772,773,774,775,776,777,778,779,780,781,782,783,784,785,790,786,787,788,789,791,792,793,794,795,796,797,798,799,800,802,801,803,804,805,806,807,808,809,810,813,811,812,814,815,816,817,818,819,820,821,823,822,934,824,825,826,827,828,829,830,831,832,833,834,836,835,837,838,839,840,841,842,843,844,846,845,847,848,849,850,851,852,853,854,855,859,856,857,858,860,861,862,864,863,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,908,909,910,911,912,913,914,915,916,917,919,918,920,921,922,923,924,925,926,927,928,929,930,931,932,933,105,448,444,431,447,440,438,437,436,433,434,442,435,432,439,445,446,441,443,213,220,218,221,212,219,222,211,242,214,215,217,241,238,240,235,237,239,234,236,216,210,229,228,226,230,227,232,233,225,224,223,231,209,193,194,182,187,186,181,192,188,190,191,189,252,258,262,257,256,261,260,255,254,253,251,259,250,185,184,183,152,151,155,153,154,313,314,315,316,142,369,366,365,357,371,353,367,356,355,368,360,370,362,364,363,354,361,372,450,352,1072,1068,1070,1071,146,1073,1075,1078,1079,1081,1082,1083,1084,1090,1091,1092,1094,1095,1093,1096,1097,1098,1099,1100,1101,1102,1103,1104,1105,1106,1108,1107,1110,1111,1109,1074,90,48,91,49,50,51,52,53,54,55,56,57,58,59,60,47,87,61,62,63,92,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,89,80,81,82,83,84,88,85,86,1112,1113,1114,95,1077,1076,93,97,1115,96,1116,1080,1117,1118,1119,379,381,380,385,386,384,383,382,286,157,147,141,94,150,172,169,171,170,168,178,173,177,174,176,175,164,165,166,162,163,167,1085,1087,1089,1088,1086,158,1032,1062,116,106,129,126,951,108,107,110,109,100,101,115,103,104,127,99,102,123,133,134,124,125,128,1030,131,111,113,118,122,121,132,112,130,119,117,120,114,1031,137,952,329,136,135,359,358,977,979,980,976,978,984,975,974,985,971,973,972,983,982,981,98,395,402,397,398,399,400,401,396,449,948,10,11,13,12,2,14,15,16,17,18,19,20,21,3,4,25,22,23,24,26,27,28,5,29,30,31,32,6,33,34,35,36,7,41,37,38,39,40,8,45,42,43,44,1,9,46,422,417,407,420,426,413,415,416,419,421,406,412,425,418,414,409,424,423,411,410,408,161,160,1033,349,1036,1037,1040,1038,1041,1045,1050,1043,1054,1046,1044,1042,1048,1049,1047,1039,1051,1053,1035,995,1052,1055,1034,1056,1057,1058,1064,321,322,350,351,993,179,1002,328,1065,1066,1059,373,375,327,376,245,325,374,377,378,243,389,390,391,392,393,394,388,387,404,405,403,1060,428,1026,148,324,1008,959,429,318,1012,207,430,180,195,287,288,312,427,317],"affectedFilesPendingEmit":[[139,1],[140,1],[143,1],[199,1],[201,1],[308,1],[206,1],[291,1],[292,1],[294,1],[296,1],[298,1],[300,1],[302,1],[303,1],[452,1],[454,1],[688,1],[1014,1],[1015,1],[1016,1],[944,1],[945,1],[1017,1],[311,1],[946,1],[947,1],[967,1],[989,1],[990,1],[1018,1],[988,1],[994,1],[1019,1],[1020,1],[1021,1],[1022,1],[208,1],[247,1],[280,1],[281,1],[282,1],[283,1],[284,1],[285,1],[949,1],[950,1],[453,1],[996,1],[997,1],[998,1],[1001,1],[1000,1],[309,1],[999,1],[686,1],[953,1],[954,1],[955,1],[956,1],[957,1],[964,1],[966,1],[958,1],[963,1],[968,1],[969,1],[970,1],[986,1],[965,1],[987,1],[451,1],[1003,1],[1004,1],[1005,1],[1011,1],[1006,1],[1010,1],[1007,1],[1013,1],[1009,1],[310,1],[991,1],[962,1],[992,1],[961,1],[332,1],[334,1],[1023,1],[1024,1],[1025,1],[333,1],[1027,1],[1028,1],[335,1],[687,1],[960,1],[337,1],[331,1],[336,1],[338,1],[1029,1],[1061,1],[159,1],[246,1],[304,1],[319,1],[305,1],[306,1],[307,1],[145,1],[156,1],[144,1],[244,1],[339,1],[340,1],[320,1],[341,1],[196,1],[326,1],[149,1],[197,1],[323,1],[330,1],[203,1],[198,1],[200,1],[342,1],[343,1],[344,1],[202,1],[299,1],[290,1],[289,1],[345,1],[293,1],[295,1],[297,1],[204,1],[301,1],[346,1],[347,1],[205,1],[348,1],[138,1],[1063,1],[1069,1],[1067,1],[268,1],[274,1],[275,1],[264,1],[248,1],[279,1],[276,1],[271,1],[249,1],[272,1],[267,1],[270,1],[273,1],[269,1],[266,1],[277,1],[265,1],[278,1],[263,1],[936,1],[939,1],[941,1],[938,1],[940,1],[937,1],[942,1],[943,1],[935,1],[691,1],[692,1],[693,1],[694,1],[699,1],[695,1],[696,1],[697,1],[698,1],[700,1],[701,1],[702,1],[703,1],[689,1],[690,1],[455,1],[456,1],[457,1],[458,1],[459,1],[460,1],[461,1],[462,1],[463,1],[464,1],[465,1],[466,1],[467,1],[468,1],[469,1],[475,1],[470,1],[471,1],[472,1],[473,1],[474,1],[476,1],[477,1],[478,1],[479,1],[480,1],[481,1],[483,1],[484,1],[482,1],[485,1],[486,1],[487,1],[488,1],[489,1],[490,1],[491,1],[492,1],[493,1],[494,1],[495,1],[496,1],[497,1],[498,1],[499,1],[500,1],[501,1],[502,1],[503,1],[504,1],[505,1],[506,1],[507,1],[508,1],[509,1],[511,1],[510,1],[512,1],[513,1],[515,1],[514,1],[516,1],[517,1],[518,1],[519,1],[520,1],[522,1],[521,1],[523,1],[524,1],[525,1],[526,1],[527,1],[528,1],[529,1],[530,1],[531,1],[532,1],[533,1],[534,1],[535,1],[536,1],[541,1],[537,1],[538,1],[539,1],[540,1],[542,1],[543,1],[544,1],[545,1],[546,1],[547,1],[548,1],[549,1],[550,1],[551,1],[553,1],[552,1],[554,1],[555,1],[556,1],[557,1],[558,1],[559,1],[560,1],[561,1],[564,1],[562,1],[563,1],[565,1],[566,1],[567,1],[568,1],[569,1],[570,1],[571,1],[572,1],[574,1],[573,1],[685,1],[575,1],[576,1],[577,1],[578,1],[579,1],[580,1],[581,1],[582,1],[583,1],[584,1],[585,1],[587,1],[586,1],[588,1],[589,1],[590,1],[591,1],[592,1],[593,1],[594,1],[595,1],[597,1],[596,1],[598,1],[599,1],[600,1],[601,1],[602,1],[603,1],[604,1],[605,1],[606,1],[610,1],[607,1],[608,1],[609,1],[611,1],[612,1],[613,1],[615,1],[614,1],[616,1],[617,1],[618,1],[619,1],[620,1],[621,1],[622,1],[623,1],[624,1],[625,1],[626,1],[627,1],[628,1],[629,1],[630,1],[631,1],[632,1],[633,1],[634,1],[635,1],[636,1],[637,1],[638,1],[639,1],[640,1],[641,1],[642,1],[643,1],[644,1],[645,1],[646,1],[647,1],[648,1],[649,1],[650,1],[651,1],[652,1],[653,1],[654,1],[655,1],[656,1],[657,1],[658,1],[659,1],[660,1],[661,1],[662,1],[663,1],[664,1],[665,1],[666,1],[667,1],[668,1],[670,1],[669,1],[671,1],[672,1],[673,1],[674,1],[675,1],[676,1],[677,1],[678,1],[679,1],[680,1],[681,1],[682,1],[683,1],[684,1],[704,1],[705,1],[706,1],[707,1],[708,1],[709,1],[710,1],[711,1],[712,1],[713,1],[714,1],[715,1],[716,1],[717,1],[718,1],[724,1],[719,1],[720,1],[721,1],[722,1],[723,1],[725,1],[726,1],[727,1],[728,1],[729,1],[730,1],[732,1],[733,1],[731,1],[734,1],[735,1],[736,1],[737,1],[738,1],[739,1],[740,1],[741,1],[742,1],[743,1],[744,1],[745,1],[746,1],[747,1],[748,1],[749,1],[750,1],[751,1],[752,1],[753,1],[754,1],[755,1],[756,1],[757,1],[758,1],[760,1],[759,1],[761,1],[762,1],[764,1],[763,1],[765,1],[766,1],[767,1],[768,1],[769,1],[771,1],[770,1],[772,1],[773,1],[774,1],[775,1],[776,1],[777,1],[778,1],[779,1],[780,1],[781,1],[782,1],[783,1],[784,1],[785,1],[790,1],[786,1],[787,1],[788,1],[789,1],[791,1],[792,1],[793,1],[794,1],[795,1],[796,1],[797,1],[798,1],[799,1],[800,1],[802,1],[801,1],[803,1],[804,1],[805,1],[806,1],[807,1],[808,1],[809,1],[810,1],[813,1],[811,1],[812,1],[814,1],[815,1],[816,1],[817,1],[818,1],[819,1],[820,1],[821,1],[823,1],[822,1],[934,1],[824,1],[825,1],[826,1],[827,1],[828,1],[829,1],[830,1],[831,1],[832,1],[833,1],[834,1],[836,1],[835,1],[837,1],[838,1],[839,1],[840,1],[841,1],[842,1],[843,1],[844,1],[846,1],[845,1],[847,1],[848,1],[849,1],[850,1],[851,1],[852,1],[853,1],[854,1],[855,1],[859,1],[856,1],[857,1],[858,1],[860,1],[861,1],[862,1],[864,1],[863,1],[865,1],[866,1],[867,1],[868,1],[869,1],[870,1],[871,1],[872,1],[873,1],[874,1],[875,1],[876,1],[877,1],[878,1],[879,1],[880,1],[881,1],[882,1],[883,1],[884,1],[885,1],[886,1],[887,1],[888,1],[889,1],[890,1],[891,1],[892,1],[893,1],[894,1],[895,1],[896,1],[897,1],[898,1],[899,1],[900,1],[901,1],[902,1],[903,1],[904,1],[905,1],[906,1],[907,1],[908,1],[909,1],[910,1],[911,1],[912,1],[913,1],[914,1],[915,1],[916,1],[917,1],[919,1],[918,1],[920,1],[921,1],[922,1],[923,1],[924,1],[925,1],[926,1],[927,1],[928,1],[929,1],[930,1],[931,1],[932,1],[933,1],[105,1],[448,1],[444,1],[431,1],[447,1],[440,1],[438,1],[437,1],[436,1],[433,1],[434,1],[442,1],[435,1],[432,1],[439,1],[445,1],[446,1],[441,1],[443,1],[213,1],[220,1],[218,1],[221,1],[212,1],[219,1],[222,1],[211,1],[242,1],[214,1],[215,1],[217,1],[241,1],[238,1],[240,1],[235,1],[237,1],[239,1],[234,1],[236,1],[216,1],[210,1],[229,1],[228,1],[226,1],[230,1],[227,1],[232,1],[233,1],[225,1],[224,1],[223,1],[231,1],[1121,1],[1122,1],[1123,1],[1124,1],[1125,1],[1126,1],[1127,1],[1128,1],[1129,1],[1130,1],[1131,1],[1132,1],[1133,1],[1134,1],[1135,1],[1136,1],[1137,1],[1138,1],[1139,1],[1140,1],[1141,1],[1142,1],[1143,1],[1144,1],[1145,1],[1146,1],[1147,1],[1148,1],[1149,1],[1150,1],[1151,1],[1152,1],[209,1],[193,1],[194,1],[182,1],[187,1],[186,1],[1120,1],[1153,1],[181,1],[192,1],[188,1],[190,1],[191,1],[189,1],[252,1],[258,1],[262,1],[257,1],[256,1],[261,1],[260,1],[255,1],[254,1],[253,1],[251,1],[259,1],[250,1],[185,1],[184,1],[183,1],[152,1],[151,1],[155,1],[153,1],[154,1],[313,1],[314,1],[315,1],[316,1],[142,1],[369,1],[366,1],[365,1],[357,1],[371,1],[353,1],[367,1],[356,1],[355,1],[368,1],[360,1],[370,1],[362,1],[364,1],[363,1],[354,1],[361,1],[372,1],[450,1],[352,1],[1072,1],[1068,1],[1070,1],[1071,1],[146,1],[1073,1],[1075,1],[1078,1],[1079,1],[1081,1],[1082,1],[1083,1],[1084,1],[1090,1],[1091,1],[1092,1],[1094,1],[1095,1],[1093,1],[1096,1],[1097,1],[1098,1],[1099,1],[1100,1],[1101,1],[1102,1],[1103,1],[1104,1],[1105,1],[1106,1],[1108,1],[1107,1],[1110,1],[1111,1],[1109,1],[1074,1],[90,1],[48,1],[91,1],[49,1],[50,1],[51,1],[52,1],[53,1],[54,1],[55,1],[56,1],[57,1],[58,1],[59,1],[60,1],[47,1],[87,1],[61,1],[62,1],[63,1],[92,1],[64,1],[65,1],[66,1],[67,1],[68,1],[69,1],[70,1],[71,1],[72,1],[73,1],[74,1],[75,1],[76,1],[77,1],[78,1],[79,1],[89,1],[80,1],[81,1],[82,1],[83,1],[84,1],[88,1],[85,1],[86,1],[1112,1],[1113,1],[1114,1],[95,1],[1077,1],[1076,1],[93,1],[97,1],[1115,1],[96,1],[1116,1],[1080,1],[1117,1],[1118,1],[1119,1],[379,1],[381,1],[380,1],[385,1],[386,1],[384,1],[383,1],[382,1],[286,1],[157,1],[147,1],[141,1],[94,1],[150,1],[172,1],[169,1],[171,1],[170,1],[168,1],[178,1],[173,1],[177,1],[174,1],[176,1],[175,1],[164,1],[165,1],[166,1],[162,1],[163,1],[167,1],[1085,1],[1087,1],[1089,1],[1088,1],[1086,1],[158,1],[1032,1],[1062,1],[116,1],[106,1],[129,1],[126,1],[951,1],[108,1],[107,1],[110,1],[109,1],[100,1],[101,1],[115,1],[103,1],[104,1],[127,1],[99,1],[102,1],[123,1],[133,1],[134,1],[124,1],[125,1],[128,1],[1030,1],[131,1],[111,1],[113,1],[118,1],[122,1],[121,1],[132,1],[112,1],[130,1],[119,1],[117,1],[120,1],[114,1],[1031,1],[137,1],[952,1],[329,1],[136,1],[135,1],[359,1],[358,1],[977,1],[979,1],[980,1],[976,1],[978,1],[984,1],[975,1],[974,1],[985,1],[971,1],[973,1],[972,1],[983,1],[982,1],[981,1],[98,1],[395,1],[402,1],[397,1],[398,1],[399,1],[400,1],[401,1],[396,1],[449,1],[948,1],[10,1],[11,1],[13,1],[12,1],[2,1],[14,1],[15,1],[16,1],[17,1],[18,1],[19,1],[20,1],[21,1],[3,1],[4,1],[25,1],[22,1],[23,1],[24,1],[26,1],[27,1],[28,1],[5,1],[29,1],[30,1],[31,1],[32,1],[6,1],[33,1],[34,1],[35,1],[36,1],[7,1],[41,1],[37,1],[38,1],[39,1],[40,1],[8,1],[45,1],[42,1],[43,1],[44,1],[1,1],[9,1],[46,1],[422,1],[417,1],[407,1],[420,1],[426,1],[413,1],[415,1],[416,1],[419,1],[421,1],[406,1],[412,1],[425,1],[418,1],[414,1],[409,1],[424,1],[423,1],[411,1],[410,1],[408,1],[161,1],[160,1],[1033,1],[349,1],[1036,1],[1037,1],[1040,1],[1038,1],[1041,1],[1045,1],[1050,1],[1043,1],[1054,1],[1046,1],[1044,1],[1042,1],[1048,1],[1049,1],[1047,1],[1039,1],[1051,1],[1053,1],[1035,1],[995,1],[1052,1],[1055,1],[1034,1],[1056,1],[1057,1],[1058,1],[1064,1],[321,1],[322,1],[350,1],[351,1],[993,1],[179,1],[1002,1],[328,1],[1065,1],[1066,1],[1059,1],[373,1],[375,1],[327,1],[376,1],[245,1],[325,1],[374,1],[377,1],[378,1],[243,1],[389,1],[390,1],[391,1],[392,1],[393,1],[394,1],[388,1],[387,1],[404,1],[405,1],[403,1],[1060,1],[428,1],[1026,1],[148,1],[324,1],[1008,1],[959,1],[429,1],[318,1],[1012,1],[207,1],[430,1],[180,1],[195,1],[287,1],[288,1],[312,1],[427,1],[317,1]]},"version":"4.5.4"} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 865dbd6e6f..43d6e87a9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1310,6 +1310,23 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== +"@types/react-virtualized@^9.21.15": + version "9.21.15" + resolved "https://registry.yarnpkg.com/@types/react-virtualized/-/react-virtualized-9.21.15.tgz#349a4f9774504e514ea4c8ebbbfe6cc8db17f045" + integrity sha512-R4ntUW+Y/a7RgRpfeYz3iRe+kaDWtXieMeQum4AoYjjZsR/QhpKqFu4muSBhzA7OHJHd6qA0KkeTzxj5ah5tmQ== + dependencies: + "@types/prop-types" "*" + "@types/react" "*" + +"@types/react@*": + version "17.0.37" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.37.tgz#6884d0aa402605935c397ae689deed115caad959" + integrity sha512-2FS1oTqBGcH/s0E+CjrCCR9+JMpsu9b69RTFO+40ua43ZqP5MmQ4iUde/dMjWR909KxZwmOQIFq6AV6NjEG5xg== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/react@^17.0.1": version "17.0.3" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79" From afd34f548e34fa0510bd1482769f0e8f6e2b8281 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Wed, 22 Dec 2021 17:08:47 +0900 Subject: [PATCH 043/204] update uxd-client 3.6 --- package.json | 2 +- tools/sdk/uxdProtocol/uxdIdl.ts | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 79597b8255..2c4e697f64 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@solana/wallet-adapter-sollet": "^0.8.0", "@solana/web3.js": "^1.30.2", "@tippyjs/react": "^4.2.5", - "@uxdprotocol/uxd-client": "^3.1.0", + "@uxdprotocol/uxd-client": "^3.6.0", "axios": "^0.21.1", "bignumber.js": "^9.0.1", "immer": "^9.0.1", diff --git a/tools/sdk/uxdProtocol/uxdIdl.ts b/tools/sdk/uxdProtocol/uxdIdl.ts index 11563a8b9f..cfea046d70 100644 --- a/tools/sdk/uxdProtocol/uxdIdl.ts +++ b/tools/sdk/uxdProtocol/uxdIdl.ts @@ -1,6 +1,6 @@ import { Idl } from '@project-serum/anchor' -export const UXD_PROGRAM_ID = 'DdJ9BKWG8XqQaj5BMjYks6Y8Amcv1onoo7cvVipp9jKC' //'UXDQDbkAeGMPR7gqDykDNu22D9DnYrKdvZhvNmMu6QX' +export const UXD_PROGRAM_ID = '3Kk1FnBEFt58yenonsXg7wQ7kJBtqDA7f83pDcsKxrZz' //'UXDQDbkAeGMPR7gqDykDNu22D9DnYrKdvZhvNmMu6QX' const uxdIdl: Idl = { version: '0.0.0', diff --git a/yarn.lock b/yarn.lock index 43d6e87a9d..b7f70d9c73 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1435,10 +1435,10 @@ "@typescript-eslint/types" "5.7.0" eslint-visitor-keys "^3.0.0" -"@uxdprotocol/uxd-client@^3.1.0": - version "3.2.0" - resolved "https://npm.pkg.github.com/download/@uxdprotocol/uxd-client/3.2.0/8588d6be3ef0a2e17f2b740dfcf31e6e0a0628da6af067d1c63d75cc19c51c2f#2199bf5eb4f7f0c1f93f772b392465c5d8bf5c92" - integrity sha512-I/iVOKhp1BXOvIyal4XHiJBE5klLtIDPj56N0pHd2RJ7RB6jZnkTLqvfBEpUdIePsIzlhyJ+lPRShytXhGoxjg== +"@uxdprotocol/uxd-client@^3.6.0": + version "3.6.0" + resolved "https://npm.pkg.github.com/download/@uxdprotocol/uxd-client/3.6.0/84a0d5e222396364604ba7b2b99ba2ccd94acef316885c0738eeafcc11eed0bf#932810a3a2e20eb0e819de0f11a268696e98a650" + integrity sha512-2/1qeDNX9ZwbZ7QN10+UhXnH2xWEtHyx+UqmB/DzbcR3UMWZoiY6n2NN+wg1L7BURGfDlXalamaYAswJR7ShYw== dependencies: "@blockworks-foundation/mango-client" "3.2.15" From 2838304f1c59d1aecf965b0c44618c521f7d4684 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 6 Jan 2022 00:01:07 +0900 Subject: [PATCH 044/204] switch to uxd dao v1 on mainnet-beta --- public/realms/mainnet-beta.json | 6 +- yarn.lock | 102 ++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/public/realms/mainnet-beta.json b/public/realms/mainnet-beta.json index 69281b3b94..adc2c64ab0 100644 --- a/public/realms/mainnet-beta.json +++ b/public/realms/mainnet-beta.json @@ -139,8 +139,8 @@ { "symbol": "UXD", "displayName": "UXD Protocol DAO", - "programId": "UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa", - "realmId": "GGt3TagE3111gd7ChWMczWDsCnkEriXyjRPzQDejJbNK", + "programId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", + "realmId": "GBXLYo4ycRNfzuzYeudu6y2ng4afNeW14WcpM2E4JJSL", "ogImage": "/realms/UXD/img/logo.png", "website": "https://www.uxd.fi.com/", "twitter": "@UXDProtocol" @@ -215,4 +215,4 @@ "website": "https://exiledapes.academy", "twitter": "@ExiledApes" } -] +] \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 5330a81cc9..8ff8ac6528 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2365,6 +2365,11 @@ chokidar@3.5.1, chokidar@^3.5.1: optionalDependencies: fsevents "~2.3.1" +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + ci-info@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" @@ -2591,6 +2596,17 @@ cross-fetch@^3.1.4: dependencies: node-fetch "2.6.1" +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -3424,6 +3440,15 @@ fraction.js@^4.0.13: version "4.0.13" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.0.13.tgz#3c1c315fa16b35c85fffa95725a36fa729c69dfe" +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -3874,6 +3899,13 @@ is-callable@^1.1.4, is-callable@^1.2.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + is-core-module@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.3.0.tgz#d341652e3408bca69c4671b79a0954a3d349f887" @@ -3892,6 +3924,11 @@ is-decimal@^2.0.0: resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.0.tgz#db1140337809fd043a056ae40a9bd1cdc563034c" integrity sha512-QfrfjQV0LjoWQ1K1XSoEZkTAzSa14RKVMa5zg3SdAfzEmQzRM4+tbSFWb78creCeA9rNBzaZal92opi1TwPWZw== +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -4045,6 +4082,13 @@ is-weakref@^1.0.1: dependencies: call-bind "^1.0.0" +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -5253,6 +5297,11 @@ next@^12.0.4: "@next/swc-win32-ia32-msvc" "12.0.4" "@next/swc-win32-x64-msvc" "12.0.4" +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -5450,6 +5499,11 @@ os-browserify@0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + p-limit@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -5543,6 +5597,25 @@ parse5@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" +patch-package@^6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148" + integrity sha512-S0vh/ZEafZ17hbhgqdnpunKDfzHQibQizx9g8yEf5dcVk3KOflOfdufRXQX8CSEkyOQwuM/bNz1GwKvFj54kaQ== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^2.4.2" + cross-spawn "^6.0.5" + find-yarn-workspace-root "^2.0.0" + fs-extra "^7.0.1" + is-ci "^2.0.0" + klaw-sync "^6.0.0" + minimist "^1.2.0" + open "^7.4.2" + rimraf "^2.6.3" + semver "^5.6.0" + slash "^2.0.0" + tmp "^0.0.33" + path-browserify@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" @@ -5557,6 +5630,11 @@ path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -6461,6 +6539,11 @@ semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" +semver@^5.5.0, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + semver@^6.0.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -6494,12 +6577,24 @@ shallowequal@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" dependencies: shebang-regex "^3.0.0" +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" @@ -7406,6 +7501,13 @@ which-typed-array@^1.1.2: has-tostringtag "^1.0.0" is-typed-array "^1.1.7" +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" From cccb4665054945c2e8cd46ca2a90a2cfe49ea1bf Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 9 Jan 2022 02:00:02 +0900 Subject: [PATCH 045/204] add close account itx --- .../createCloseProgramAccountInstruction.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tools/sdk/uxdProtocol/createCloseProgramAccountInstruction.ts diff --git a/tools/sdk/uxdProtocol/createCloseProgramAccountInstruction.ts b/tools/sdk/uxdProtocol/createCloseProgramAccountInstruction.ts new file mode 100644 index 0000000000..85daf642b1 --- /dev/null +++ b/tools/sdk/uxdProtocol/createCloseProgramAccountInstruction.ts @@ -0,0 +1,43 @@ +import { PublicKey, TransactionInstruction } from '@solana/web3.js' +import { BPF_UPGRADE_LOADER_ID } from '@utils/tokens' + +export async function createCloseProgramAccountInstruction( + programId: PublicKey, + receiver: PublicKey, + signer: PublicKey +): Promise { + const bpfUpgradableLoaderId = BPF_UPGRADE_LOADER_ID + const [programDataAddress] = await PublicKey.findProgramAddress( + [programId.toBuffer()], + bpfUpgradableLoaderId + ) + + const keys = [ + { + pubkey: programDataAddress, + isWritable: true, + isSigner: false, + }, + { + pubkey: receiver, + isWritable: true, + isSigner: false, + }, + { + pubkey: signer, + isWritable: false, + isSigner: true, + }, + { + pubkey: programId, + isWritable: true, + isSigner: false, + }, + ] + + return new TransactionInstruction({ + keys, + programId: bpfUpgradableLoaderId, + data: Buffer.from([5, 0, 0, 0]), // CloseAccount instruction bincode + }) +} From 95db587facefbe7380b498fc2bf27ae71a6aaa95 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 9 Jan 2022 02:23:23 +0900 Subject: [PATCH 046/204] update packages.json, lock and small fix --- hooks/useGovernanceAssets.ts | 131 ++++++++++++++++++----------------- package.json | 2 + yarn.lock | 91 ++++++++++++++---------- 3 files changed, 124 insertions(+), 100 deletions(-) diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 36873f22c5..bb16f98afc 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -81,6 +81,71 @@ export default function useGovernanceAssets() { ownVoterWeight.canCreateProposal(g.info.config) ) + const getAvailableInstructions = () => { + return availableInstructions.filter((x) => x.isVisible) + } + function prepareTokenGovernances() { + const tokenGovernances = getGovernancesByAccountType( + GovernanceAccountType.TokenGovernance + ) + const governedTokenAccounts: GovernedTokenAccount[] = [] + for (const i of tokenGovernances) { + const realmTokenAccount = realmTokenAccounts.find( + (x) => x.publicKey.toBase58() === i.info.governedAccount.toBase58() + ) + const mint = tokenMints.find( + (x) => + realmTokenAccount?.account.mint.toBase58() === x.publicKey.toBase58() + ) + const obj = { + governance: i, + token: realmTokenAccount, + mint, + isNft: mint?.publicKey.toBase58() === DEFAULT_NFT_TREASURY_MINT, + } + governedTokenAccounts.push(obj) + } + return governedTokenAccounts + } + async function getMintWithGovernances() { + const mintGovernances = getGovernancesByAccountType( + GovernanceAccountType.MintGovernance + ) + const governedMintInfoAccounts: GovernedMintInfoAccount[] = [] + const mintGovernancesMintInfo = await getMultipleAccountInfoChunked( + connection, + mintGovernances.map((x) => x.info.governedAccount) + ) + mintGovernancesMintInfo.forEach((mintAccountInfo, index) => { + const governance = mintGovernances[index] + if (!mintAccountInfo) { + throw new Error( + `Missing mintAccountInfo for: ${governance.pubkey.toBase58()}` + ) + } + const data = Buffer.from(mintAccountInfo.data) + const parsedMintInfo = parseMintAccountData(data) as MintInfo + const obj = { + governance, + mintInfo: parsedMintInfo, + } + governedMintInfoAccounts.push(obj) + }) + return governedMintInfoAccounts + } + const governedTokenAccounts = prepareTokenGovernances() + const governedTokenAccountsWithoutNfts = governedTokenAccounts.filter( + (x) => x.mint?.publicKey.toBase58() !== DEFAULT_NFT_TREASURY_MINT + ) + const nftsGovernedTokenAccounts = governedTokenAccounts.filter( + (x) => x.mint?.publicKey.toBase58() === DEFAULT_NFT_TREASURY_MINT + ) + const canUseTokenTransferInstruction = governedTokenAccountsWithoutNfts.some( + (g) => + g.governance && + ownVoterWeight.canCreateProposal(g.governance?.info?.config) + ) + const availableInstructions = [ { id: Instructions.InitializeController, @@ -115,7 +180,7 @@ export default function useGovernanceAssets() { { id: Instructions.Transfer, name: 'Transfer Tokens', - isVisible: canUseTransferInstruction, + isVisible: canUseTokenTransferInstruction, }, { id: Instructions.ProgramUpgrade, @@ -147,70 +212,6 @@ export default function useGovernanceAssets() { ), }, ] - const getAvailableInstructions = () => { - return availableInstructions.filter((x) => x.isVisible) - } - function prepareTokenGovernances() { - const tokenGovernances = getGovernancesByAccountType( - GovernanceAccountType.TokenGovernance - ) - const governedTokenAccounts: GovernedTokenAccount[] = [] - for (const i of tokenGovernances) { - const realmTokenAccount = realmTokenAccounts.find( - (x) => x.publicKey.toBase58() === i.info.governedAccount.toBase58() - ) - const mint = tokenMints.find( - (x) => - realmTokenAccount?.account.mint.toBase58() === x.publicKey.toBase58() - ) - const obj = { - governance: i, - token: realmTokenAccount, - mint, - isNft: mint?.publicKey.toBase58() === DEFAULT_NFT_TREASURY_MINT, - } - governedTokenAccounts.push(obj) - } - return governedTokenAccounts - } - async function getMintWithGovernances() { - const mintGovernances = getGovernancesByAccountType( - GovernanceAccountType.MintGovernance - ) - const governedMintInfoAccounts: GovernedMintInfoAccount[] = [] - const mintGovernancesMintInfo = await getMultipleAccountInfoChunked( - connection, - mintGovernances.map((x) => x.info.governedAccount) - ) - mintGovernancesMintInfo.forEach((mintAccountInfo, index) => { - const governance = mintGovernances[index] - if (!mintAccountInfo) { - throw new Error( - `Missing mintAccountInfo for: ${governance.pubkey.toBase58()}` - ) - } - const data = Buffer.from(mintAccountInfo.data) - const parsedMintInfo = parseMintAccountData(data) as MintInfo - const obj = { - governance, - mintInfo: parsedMintInfo, - } - governedMintInfoAccounts.push(obj) - }) - return governedMintInfoAccounts - } - const governedTokenAccounts = prepareTokenGovernances() - const governedTokenAccountsWithoutNfts = governedTokenAccounts.filter( - (x) => x.mint?.publicKey.toBase58() !== DEFAULT_NFT_TREASURY_MINT - ) - const nftsGovernedTokenAccounts = governedTokenAccounts.filter( - (x) => x.mint?.publicKey.toBase58() === DEFAULT_NFT_TREASURY_MINT - ) - const canUseTokenTransferInstruction = governedTokenAccountsWithoutNfts.some( - (g) => - g.governance && - ownVoterWeight.canCreateProposal(g.governance?.info?.config) - ) return { governancesArray, diff --git a/package.json b/package.json index 7da24b4ada..9bb8bac345 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "@headlessui/react": "^1.0.0", "@heroicons/react": "^1.0.1", "@project-serum/anchor": "^0.19.0", + "@metaplex/js": "^4.10.1", + "@nfteyez/sol-rayz": "^0.8.0", "@project-serum/borsh": "^0.2.2", "@project-serum/common": "^0.0.1-beta.3", "@project-serum/sol-wallet-adapter": "^0.2.0", diff --git a/yarn.lock b/yarn.lock index d3336e52b4..64746711b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1361,27 +1361,7 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solana/web3.js@^1.12.0", "@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.31.0": - version "1.31.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.31.0.tgz#7a313d4c1a90b77f27ddbfe845a10d6883e06452" - integrity sha512-7nHHx1JNFnrt15e9y8m38I/EJCbaB+bFC3KZVM1+QhybCikFxGMtGA5r7PDC3GEL1R2RZA8yKoLkDKo3vzzqnw== - dependencies: - "@babel/runtime" "^7.12.5" - "@ethersproject/sha2" "^5.5.0" - "@solana/buffer-layout" "^3.0.0" - bn.js "^5.0.0" - borsh "^0.4.0" - bs58 "^4.0.1" - buffer "6.0.1" - cross-fetch "^3.1.4" - jayson "^3.4.4" - js-sha3 "^0.8.0" - rpc-websockets "^7.4.2" - secp256k1 "^4.0.2" - superstruct "^0.14.2" - tweetnacl "^1.0.0" - -"@solana/web3.js@^1.30.2": +"@solana/web3.js@^1.12.0", "@solana/web3.js@^1.17.0", "@solana/web3.js@^1.2.2", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.30.2", "@solana/web3.js@^1.31.0": version "1.31.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.31.0.tgz#7a313d4c1a90b77f27ddbfe845a10d6883e06452" integrity sha512-7nHHx1JNFnrt15e9y8m38I/EJCbaB+bFC3KZVM1+QhybCikFxGMtGA5r7PDC3GEL1R2RZA8yKoLkDKo3vzzqnw== @@ -2566,6 +2546,11 @@ buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + buffer-layout@^1.2.0, buffer-layout@^1.2.1, buffer-layout@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5" @@ -2769,6 +2754,11 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + ci-info@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" @@ -3084,7 +3074,7 @@ cross-fetch@^3.1.4: dependencies: node-fetch "2.6.1" -cross-spawn@^6.0.5: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -3511,6 +3501,11 @@ dotenv@10.0.0, dotenv@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== +dotenv@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + dset@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/dset/-/dset-2.1.0.tgz#cd1e99e55cf32366d8f144f906c42f7fb3bf431e" @@ -4962,10 +4957,6 @@ is-decimal@^2.0.0: resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.0.tgz#db1140337809fd043a056ae40a9bd1cdc563034c" integrity sha512-QfrfjQV0LjoWQ1K1XSoEZkTAzSa14RKVMa5zg3SdAfzEmQzRM4+tbSFWb78creCeA9rNBzaZal92opi1TwPWZw== -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -4984,6 +4975,11 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -5199,6 +5195,16 @@ is-weakref@^1.0.1: dependencies: call-bind "^1.0.0" +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + is-wsl@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -5206,6 +5212,11 @@ is-wsl@^2.1.1: dependencies: is-docker "^2.0.0" +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -6840,6 +6851,14 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + opn@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" @@ -6885,6 +6904,7 @@ os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -7002,6 +7022,16 @@ parse5@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + patch-package@^6.4.7: version "6.4.7" resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148" @@ -7020,15 +7050,6 @@ patch-package@^6.4.7: semver "^5.6.0" slash "^2.0.0" tmp "^0.0.33" -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= path-browserify@1.0.1: version "1.0.1" From d816642cf9a54eebd37596e0a310a6807f70820d Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 13 Jan 2022 21:27:20 +0900 Subject: [PATCH 047/204] update lock --- yarn.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index 047c012cb4..d18978e987 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1134,10 +1134,10 @@ resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.9.3.tgz" integrity sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ== -"@project-serum/anchor@^0.10.0": - version "0.10.0" - resolved "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.10.0.tgz" - integrity sha512-FWw3bQy1otINPq40uOTX0h8eE3drnespaq9Xkwpy3UXj6i46xuLjsWC+x9hp76EN4pC174POeNPvB0aivatswg== +"@project-serum/anchor@^0.11.1": + version "0.11.1" + resolved "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.11.1.tgz" + integrity sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA== dependencies: "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" @@ -1154,10 +1154,10 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/anchor@^0.11.1": - version "0.11.1" - resolved "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.11.1.tgz" - integrity sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA== +"@project-serum/anchor@^0.16.2": + version "0.16.2" + resolved "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.16.2.tgz" + integrity sha512-wOJwObd4wOZ5tRRMCKYjeMNsEmf7vuC71KQRnw6wthhErL8c/818n4gYIZCf/1ZPl/8WPruIlmtQHDSEyy2+0Q== dependencies: "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" @@ -1174,10 +1174,10 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/anchor@^0.16.2": - version "0.16.2" - resolved "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.16.2.tgz" - integrity sha512-wOJwObd4wOZ5tRRMCKYjeMNsEmf7vuC71KQRnw6wthhErL8c/818n4gYIZCf/1ZPl/8WPruIlmtQHDSEyy2+0Q== +"@project-serum/anchor@^0.19.0": + version "0.19.0" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.19.0.tgz#79f1fbe7c3134860ccbfe458a0e09daf79644885" + integrity sha512-cs0LBmJOrL9eJ8MRNqitnzbpCT5QEzVdJmiIjfNV5YaGn1K9vISR7DtISj3Bdl3KBdLqii4CTw1mpHdi8iXUCg== dependencies: "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" @@ -2607,7 +2607,7 @@ buffer-indexof@^1.0.0: resolved "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz" integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== -buffer-layout@^1.2.0, buffer-layout@^1.2.1: +buffer-layout@^1.2.0, buffer-layout@^1.2.1, buffer-layout@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz" integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA== From ff2b3e846816b25dff79db911c111789892735dd Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 13 Jan 2022 21:40:26 +0900 Subject: [PATCH 048/204] update uxd instructions to work with @solana/spl-governance --- .../DepositInsuranceToMangoDepository.tsx | 15 +++++++++------ .../instructions/InitializeController.tsx | 15 +++++++++------ .../instructions/RegisterMangoDepository.tsx | 15 +++++++++------ .../SetMangoDepositoriesRedeemableSoftCap.tsx | 15 +++++++++------ .../instructions/SetRedeemGlobalSupplyCap.tsx | 15 +++++++++------ .../WithdrawInsuranceFromMangoDepository.tsx | 15 +++++++++------ 6 files changed, 54 insertions(+), 36 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx index e85d89f2e8..3a90a8f08c 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx @@ -10,10 +10,7 @@ import { } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' -import { Governance, GovernanceAccountType } from '@models/accounts' -import { ParsedAccount } from '@models/core/accounts' import useWalletStore from 'stores/useWalletStore' -import { serializeInstructionToBase64 } from '@models/serialisation' import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' import GovernedAccountSelect from '../GovernedAccountSelect' @@ -24,13 +21,19 @@ import { getDepositoryMintSymbols, getInsuranceMintSymbols, } from '@tools/sdk/uxdProtocol/uxdClient' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, + GovernanceAccountType, +} from '@solana/spl-governance' const DepositInsuranceToMangoDepository = ({ index, governance, }: { index: number - governance: ParsedAccount | null + governance: ProgramAccount | null }) => { const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) @@ -69,12 +72,12 @@ const DepositInsuranceToMangoDepository = ({ if ( isValid && programId && - form.governedAccount?.governance?.info && + form.governedAccount?.governance?.account && wallet?.publicKey ) { const createIx = await createDepositInsuranceToMangoDepositoryInstruction( connection, - form.governedAccount?.governance.info.governedAccount, + form.governedAccount?.governance.account.governedAccount, form.governedAccount?.governance.pubkey, form.collateralName, form.insuranceName, diff --git a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx index f3811819cb..51e74b4755 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx @@ -10,21 +10,24 @@ import { } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' -import { Governance, GovernanceAccountType } from '@models/accounts' -import { ParsedAccount } from '@models/core/accounts' import useWalletStore from 'stores/useWalletStore' -import { serializeInstructionToBase64 } from '@models/serialisation' import Input from '@components/inputs/Input' import GovernedAccountSelect from '../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createInitializeControllerInstruction from '@tools/sdk/uxdProtocol/createInitializeControllerInstruction' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, + GovernanceAccountType, +} from '@solana/spl-governance' const InitializeController = ({ index, governance, }: { index: number - governance: ParsedAccount | null + governance: ProgramAccount | null }) => { const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) @@ -61,12 +64,12 @@ const InitializeController = ({ if ( isValid && programId && - form.governedAccount?.governance?.info && + form.governedAccount?.governance?.account && form.mintDecimals && wallet?.publicKey ) { const initializeControllerIx = createInitializeControllerInstruction( - form.governedAccount?.governance.info.governedAccount, + form.governedAccount?.governance.account.governedAccount, form.mintDecimals, form.governedAccount?.governance.pubkey, new PublicKey(wallet.publicKey.toBase58()), diff --git a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx index 7a7cce2914..9a771183b9 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx @@ -10,10 +10,7 @@ import { } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' -import { Governance, GovernanceAccountType } from '@models/accounts' -import { ParsedAccount } from '@models/core/accounts' import useWalletStore from 'stores/useWalletStore' -import { serializeInstructionToBase64 } from '@models/serialisation' import { debounce } from '@utils/debounce' import GovernedAccountSelect from '../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' @@ -23,13 +20,19 @@ import { getDepositoryMintSymbols, getInsuranceMintSymbols, } from '@tools/sdk/uxdProtocol/uxdClient' +import { + ProgramAccount, + Governance, + GovernanceAccountType, + serializeInstructionToBase64, +} from '@solana/spl-governance' const RegisterMangoDepository = ({ index, governance, }: { index: number - governance: ParsedAccount | null + governance: ProgramAccount | null }) => { const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) @@ -67,13 +70,13 @@ const RegisterMangoDepository = ({ if ( isValid && programId && - form.governedAccount?.governance?.info && + form.governedAccount?.governance?.account && form.insuranceName && wallet?.publicKey ) { const createIx = await createRegisterMangoDepositoryInstruction( connection, - form.governedAccount?.governance.info.governedAccount, + form.governedAccount?.governance.account.governedAccount, form.governedAccount?.governance.pubkey, new PublicKey(wallet.publicKey.toBase58()), form.collateralName, diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx index 50ea77bd68..d2da68b6d7 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -10,22 +10,25 @@ import { } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' -import { Governance, GovernanceAccountType } from '@models/accounts' -import { ParsedAccount } from '@models/core/accounts' import useWalletStore from 'stores/useWalletStore' -import { serializeInstructionToBase64 } from '@models/serialisation' import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' import GovernedAccountSelect from '../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createSetMangoDepositoriesRedeemableSoftCapInstruction from '@tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, + GovernanceAccountType, +} from '@solana/spl-governance' const SetMangoDepositoriesRedeemableSoftCap = ({ index, governance, }: { index: number - governance: ParsedAccount | null + governance: ProgramAccount | null }) => { const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) @@ -63,12 +66,12 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ if ( isValid && programId && - form.governedAccount?.governance?.info && + form.governedAccount?.governance?.account && wallet?.publicKey ) { const createIx = createSetMangoDepositoriesRedeemableSoftCapInstruction( connection.current, - form.governedAccount.governance?.info.governedAccount, + form.governedAccount.governance?.account.governedAccount, form.softCap, form.governedAccount?.governance.pubkey, wallet diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx index 624107fe81..9e0b8adee7 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx @@ -10,22 +10,25 @@ import { } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' -import { Governance, GovernanceAccountType } from '@models/accounts' -import { ParsedAccount } from '@models/core/accounts' import useWalletStore from 'stores/useWalletStore' -import { serializeInstructionToBase64 } from '@models/serialisation' import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' import GovernedAccountSelect from '../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createSetRedeemableGlobalSupplyCapInstruction from '@tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, + GovernanceAccountType, +} from '@solana/spl-governance' const SetRedeemGlobalSupplyCap = ({ index, governance, }: { index: number - governance: ParsedAccount | null + governance: ProgramAccount | null }) => { const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) @@ -62,12 +65,12 @@ const SetRedeemGlobalSupplyCap = ({ if ( isValid && programId && - form.governedAccount?.governance?.info && + form.governedAccount?.governance?.account && wallet?.publicKey ) { const createIx = createSetRedeemableGlobalSupplyCapInstruction( connection.current, - form.governedAccount.governance?.info.governedAccount, + form.governedAccount.governance?.account.governedAccount, form.supplyCap, form.governedAccount?.governance.pubkey, wallet diff --git a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx index 3097db41db..ae676093f2 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx @@ -10,10 +10,13 @@ import { } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' -import { Governance, GovernanceAccountType } from '@models/accounts' -import { ParsedAccount } from '@models/core/accounts' import useWalletStore from 'stores/useWalletStore' -import { serializeInstructionToBase64 } from '@models/serialisation' +import { + serializeInstructionToBase64, + Governance, + GovernanceAccountType, + ProgramAccount, +} from '@solana/spl-governance' import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' import GovernedAccountSelect from '../GovernedAccountSelect' @@ -30,7 +33,7 @@ const WithdrawInsuranceFromMangoDepository = ({ governance, }: { index: number - governance: ParsedAccount | null + governance: ProgramAccount | null }) => { const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) @@ -69,12 +72,12 @@ const WithdrawInsuranceFromMangoDepository = ({ if ( isValid && programId && - form.governedAccount?.governance?.info && + form.governedAccount?.governance?.account && wallet?.publicKey ) { const createIx = await createWithdrawInsuranceFromMangoDepositoryInstruction( connection, - form.governedAccount?.governance.info.governedAccount, + form.governedAccount?.governance.account.governedAccount, form.governedAccount?.governance.pubkey, form.collateralName, form.insuranceName, From 3db99396b8e2923aabcba18684d8fe1f904f161c Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 13 Jan 2022 21:41:47 +0900 Subject: [PATCH 049/204] fix some types --- components/instructions/programs/uxdProtocol.tsx | 2 +- .../sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/instructions/programs/uxdProtocol.tsx b/components/instructions/programs/uxdProtocol.tsx index 3f618b446b..a324a406b8 100644 --- a/components/instructions/programs/uxdProtocol.tsx +++ b/components/instructions/programs/uxdProtocol.tsx @@ -1,6 +1,6 @@ import { Connection } from '@solana/web3.js' import { struct, u8, u48 } from 'buffer-layout' -import { AccountMetaData } from '../../../models/accounts' +import { AccountMetaData } from '@solana/spl-governance' export const UXD_PROGRAM_INSTRUCTIONS = { UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa: { diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index cbee1be073..385d34c0c2 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -1,4 +1,4 @@ -import { serializeInstructionToBase64 } from '@models/serialisation' +import { serializeInstructionToBase64 } from '@solana/spl-governance' import { Provider } from '@project-serum/anchor' import { Token, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token' import { SignerWalletAdapter } from '@solana/wallet-adapter-base' From 8efc116bbb175e169640ce417e43a4214051cf60 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 14 Jan 2022 00:18:48 +0900 Subject: [PATCH 050/204] lint --- utils/formatting.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/formatting.tsx b/utils/formatting.tsx index fb71bc6dea..e3089b6abe 100644 --- a/utils/formatting.tsx +++ b/utils/formatting.tsx @@ -1,9 +1,9 @@ -import BN from 'bn.js' +import { BN } from '@project-serum/anchor' import moment from 'moment' import { PublicKey } from '@solana/web3.js' const votePrecision = 10000 -export const calculatePct = (c: BN, total?: BN) => { +export const calculatePct = (c: BN = new BN(0), total?: BN) => { if (total?.isZero()) { return 0 } @@ -17,7 +17,7 @@ export const calculatePct = (c: BN, total?: BN) => { ) } -export const fmtTokenAmount = (c: BN, decimals?: number) => +export const fmtTokenAmount = (c: BN = new BN(0), decimals?: number) => c.div(new BN(10).pow(new BN(decimals ?? 0))).toNumber() export const fmtUnixTime = (d: BN) => moment.unix(d.toNumber()).fromNow() From ead7b34705d2da7df1d9ec5c0499d6c1f96561fc Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 14 Jan 2022 16:15:01 +0900 Subject: [PATCH 051/204] add SetProgramAuthority itx --- components/instructions/programs/names.ts | 1 + hooks/useGovernanceAssets.ts | 5 + .../instructions/SetProgramAuthority.tsx | 139 ++++++++++++++++++ pages/dao/[symbol]/proposal/new.tsx | 8 + ...> createSetProgramAuthorityInstruction.ts} | 13 +- utils/uiTypes/proposalCreationTypes.ts | 7 + 6 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx rename tools/sdk/bpfUpgradeableLoader/{createSetUpgradeAuthority.ts => createSetProgramAuthorityInstruction.ts} (71%) diff --git a/components/instructions/programs/names.ts b/components/instructions/programs/names.ts index 627050c66f..f698761f88 100644 --- a/components/instructions/programs/names.ts +++ b/components/instructions/programs/names.ts @@ -23,6 +23,7 @@ export const PROGRAM_NAMES = { EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q: 'Raydium Staking Program', UXDQDbkAeGMPR7gqDykDNu22D9DnYrKdvZhvNmMu6QX: 'UXDProtocol Program', + UXDBdZFw33TgoKPQK2sXDCLfNrdio2gUzLs1yHePfMj: 'UXD IDO Program', SysvarRent111111111111111111111111111111111: 'Sysvar: Rent', SysvarC1ock11111111111111111111111111111111: 'Sysvar: Clock', diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 772c968a69..45f698b42d 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -187,6 +187,11 @@ export default function useGovernanceAssets() { name: 'Upgrade Program', isVisible: canUseProgramUpgradeInstruction, }, + { + id: Instructions.SetProgramAuthority, + name: 'Set Program Authority', + isVisible: canUseProgramUpgradeInstruction, + }, { id: Instructions.Mint, name: 'Mint Tokens', diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx new file mode 100644 index 0000000000..0dfb73a887 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx @@ -0,0 +1,139 @@ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + ProgramAuthorityForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import useWalletStore from 'stores/useWalletStore' +import Input from '@components/inputs/Input' +import { debounce } from '@utils/debounce' +import GovernedAccountSelect from '../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { validateInstruction } from '@utils/instructionTools' +import createSetProgramAuthorityInstruction from '@tools/sdk/bpfUpgradeableLoader/createSetProgramAuthorityInstruction' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, + GovernanceAccountType, +} from '@solana/spl-governance' + +const SetProgramAuthority = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { getGovernancesByAccountType } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountType( + GovernanceAccountType.ProgramGovernance + ).map((x) => { + return { + governance: x, + } + }) + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({ + governedAccount: undefined, + accountId: programId?.toString(), + destinationAuthority: '', + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + async function getInstruction(): Promise { + const isValid = await validateInstruction({ schema, form, setFormErrors }) + let serializedInstruction = '' + if ( + isValid && + programId && + form.governedAccount?.governance?.account && + wallet?.publicKey + ) { + const upgradeIx = await createSetProgramAuthorityInstruction( + form.governedAccount.governance.account.governedAccount, + form.governedAccount.governance.pubkey, + new PublicKey(form.destinationAuthority) + ) + serializedInstruction = serializeInstructionToBase64(upgradeIx) + } + const obj: UiInstruction = { + serializedInstruction: serializedInstruction, + isValid, + governance: form.governedAccount?.governance, + } + return obj + } + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + if (form.destinationAuthority) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.destinationAuthority]) + useEffect(() => { + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) + const schema = yup.object().shape({ + destinationAuthority: yup + .string() + .required('new authority address is required'), + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + handleSetForm({ + value: evt.target.value, + propertyName: 'destinationAuthority', + }) + } + error={formErrors['destinationAuthority']} + /> + + ) +} + +export default SetProgramAuthority diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index 63fcb4034a..6ec028e278 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -32,6 +32,7 @@ import { ProgramAccount } from '@solana/spl-governance' import { Governance, GovernanceAccountType } from '@solana/spl-governance' import InstructionContentContainer from './components/InstructionContentContainer' import ProgramUpgrade from './components/instructions/ProgramUpgrade' +import SetProgramAuthority from './components/instructions/SetProgramAuthority' import Empty from './components/instructions/Empty' import Mint from './components/instructions/Mint' import CustomBase64 from './components/instructions/CustomBase64' @@ -286,6 +287,13 @@ const New = () => { return ( ) + case Instructions.SetProgramAuthority: + return ( + + ) case Instructions.InitializeController: return case Instructions.SetRedeemableGlobalSupplyCap: diff --git a/tools/sdk/bpfUpgradeableLoader/createSetUpgradeAuthority.ts b/tools/sdk/bpfUpgradeableLoader/createSetProgramAuthorityInstruction.ts similarity index 71% rename from tools/sdk/bpfUpgradeableLoader/createSetUpgradeAuthority.ts rename to tools/sdk/bpfUpgradeableLoader/createSetProgramAuthorityInstruction.ts index b71840d37b..bdfd2fef42 100644 --- a/tools/sdk/bpfUpgradeableLoader/createSetUpgradeAuthority.ts +++ b/tools/sdk/bpfUpgradeableLoader/createSetProgramAuthorityInstruction.ts @@ -1,16 +1,16 @@ import { PublicKey, TransactionInstruction } from '@solana/web3.js' +import { BPF_UPGRADE_LOADER_ID } from '@utils/tokens' -export async function createSetUpgradeAuthority( +async function createSetProgramAuthorityInstruction( programId: PublicKey, upgradeAuthority: PublicKey, - newUpgradeAuthority: PublicKey, - bpfUpgradableLoaderId: PublicKey -) { + newUpgradeAuthority: PublicKey +): Promise { + const bpfUpgradableLoaderId = BPF_UPGRADE_LOADER_ID const [programDataAddress] = await PublicKey.findProgramAddress( [programId.toBuffer()], bpfUpgradableLoaderId ) - const keys = [ { pubkey: programDataAddress, @@ -28,10 +28,11 @@ export async function createSetUpgradeAuthority( isSigner: false, }, ] - return new TransactionInstruction({ keys, programId: bpfUpgradableLoaderId, data: Buffer.from([4, 0, 0, 0]), // SetAuthority instruction bincode }) } + +export default createSetProgramAuthorityInstruction diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 8af3a5ed5c..073b23fb5c 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -43,6 +43,12 @@ export interface ProgramUpgradeForm { bufferAddress: string } +export interface ProgramAuthorityForm { + governedAccount: GovernedProgramAccount | GovernedTokenAccount | undefined + accountId: string | undefined + destinationAuthority: string +} + export interface MangoMakeChangeMaxAccountsForm { governedAccount: GovernedProgramAccount | undefined programId: string | undefined @@ -63,6 +69,7 @@ export interface EmptyInstructionForm { export enum Instructions { Transfer, ProgramUpgrade, + SetProgramAuthority, Mint, Base64, None, From 7d97b90dae89989a83da0b2a99eca0466970a42c Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 14 Jan 2022 21:46:16 +0900 Subject: [PATCH 052/204] update uxd itxs to work with uxd-client 4.10 --- hooks/useGovernanceAssets.ts | 2 +- package.json | 4 +- .../DepositInsuranceToMangoDepository.tsx | 3 +- .../instructions/InitializeController.tsx | 5 +- .../instructions/RegisterMangoDepository.tsx | 3 +- .../SetMangoDepositoriesRedeemableSoftCap.tsx | 5 +- .../instructions/SetRedeemGlobalSupplyCap.tsx | 5 +- .../WithdrawInsuranceFromMangoDepository.tsx | 3 +- ...itInsuranceToMangoDepositoryInstruction.ts | 12 ++--- .../createInitializeControllerInstruction.ts | 9 ++-- ...reateRegisterMangoDepositoryInstruction.ts | 12 ++--- ...epositoriesRedeemableSoftCapInstruction.ts | 9 ++-- ...SetRedeemableGlobalSupplyCapInstruction.ts | 9 ++-- ...InsuranceFromMangoDepositoryInstruction.ts | 12 ++--- tools/sdk/uxdProtocol/uxdClient.ts | 48 ++----------------- yarn.lock | 30 ++++++++++-- 16 files changed, 57 insertions(+), 114 deletions(-) diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 45f698b42d..89b4a47a7c 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -72,7 +72,7 @@ export default function useGovernanceAssets() { ) const canUseUxdInstructions = - symbol === 'UXD' && + //symbol === 'UXD' && canUseGovernanceForInstruction(GovernanceAccountType.ProgramGovernance) const canUseAnyInstruction = diff --git a/package.json b/package.json index a474dd780e..374ab6210a 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@solana/wallet-adapter-sollet": "^0.8.0", "@solana/web3.js": "^1.30.2", "@tippyjs/react": "^4.2.5", - "@uxdprotocol/uxd-client": "^3.6.0", + "@uxdprotocol/uxd-client": "4.10.0", "axios": "^0.21.1", "bignumber.js": "^9.0.1", "immer": "^9.0.1", @@ -95,4 +95,4 @@ "preset": "emotion" } } -} +} \ No newline at end of file diff --git a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx index 3a90a8f08c..873fe5ed2a 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx @@ -81,8 +81,7 @@ const DepositInsuranceToMangoDepository = ({ form.governedAccount?.governance.pubkey, form.collateralName, form.insuranceName, - form.insuranceDepositedAmount, - wallet + form.insuranceDepositedAmount ) serializedInstruction = serializeInstructionToBase64(createIx) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx index 51e74b4755..cf7198d0c8 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx @@ -29,7 +29,6 @@ const InitializeController = ({ index: number governance: ProgramAccount | null }) => { - const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() const { getGovernancesByAccountType } = useGovernanceAssets() @@ -72,9 +71,7 @@ const InitializeController = ({ form.governedAccount?.governance.account.governedAccount, form.mintDecimals, form.governedAccount?.governance.pubkey, - new PublicKey(wallet.publicKey.toBase58()), - connection.current, - wallet + new PublicKey(wallet.publicKey.toBase58()) ) serializedInstruction = serializeInstructionToBase64( initializeControllerIx diff --git a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx index 9a771183b9..d648b2c9f6 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx @@ -80,8 +80,7 @@ const RegisterMangoDepository = ({ form.governedAccount?.governance.pubkey, new PublicKey(wallet.publicKey.toBase58()), form.collateralName, - form.insuranceName, - wallet + form.insuranceName ) serializedInstruction = serializeInstructionToBase64(createIx) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx index d2da68b6d7..b6f903540c 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -30,7 +30,6 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ index: number governance: ProgramAccount | null }) => { - const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() const { getGovernancesByAccountType } = useGovernanceAssets() @@ -70,11 +69,9 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ wallet?.publicKey ) { const createIx = createSetMangoDepositoriesRedeemableSoftCapInstruction( - connection.current, form.governedAccount.governance?.account.governedAccount, form.softCap, - form.governedAccount?.governance.pubkey, - wallet + form.governedAccount?.governance.pubkey ) serializedInstruction = serializeInstructionToBase64(createIx) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx index 9e0b8adee7..6d322e0102 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx @@ -30,7 +30,6 @@ const SetRedeemGlobalSupplyCap = ({ index: number governance: ProgramAccount | null }) => { - const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() const { getGovernancesByAccountType } = useGovernanceAssets() @@ -69,11 +68,9 @@ const SetRedeemGlobalSupplyCap = ({ wallet?.publicKey ) { const createIx = createSetRedeemableGlobalSupplyCapInstruction( - connection.current, form.governedAccount.governance?.account.governedAccount, form.supplyCap, - form.governedAccount?.governance.pubkey, - wallet + form.governedAccount?.governance.pubkey ) serializedInstruction = serializeInstructionToBase64(createIx) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx index ae676093f2..e8023f289d 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx @@ -81,8 +81,7 @@ const WithdrawInsuranceFromMangoDepository = ({ form.governedAccount?.governance.pubkey, form.collateralName, form.insuranceName, - form.insuranceWithdrawnAmount, - wallet + form.insuranceWithdrawnAmount ) serializedInstruction = serializeInstructionToBase64(createIx) } diff --git a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts index 5d35787f87..19a9a23604 100644 --- a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts @@ -1,5 +1,4 @@ import { Provider } from '@project-serum/anchor' -import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { Controller } from '@uxdprotocol/uxd-client' import type { ConnectionContext } from 'utils/connection' @@ -18,16 +17,11 @@ const createDepositInsuranceToMangoDepositoryInstruction = async ( authority: PublicKey, depositoryMintName: string, insuranceMintName: string, - insuranceDepositedAmount: number, - wallet: SignerWalletAdapter + insuranceDepositedAmount: number ): Promise => { - const client = uxdClient(connection.current, uxdProgramId, wallet) + const client = uxdClient(uxdProgramId) - const mango = await initializeMango( - connection.current, - connection.cluster, - wallet - ) + const mango = await initializeMango(connection.current, connection.cluster) const depository = instantiateMangoDepository( uxdProgramId, diff --git a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts index c206ca9f1f..f1345105da 100644 --- a/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts +++ b/tools/sdk/uxdProtocol/createInitializeControllerInstruction.ts @@ -1,17 +1,14 @@ import { Provider } from '@project-serum/anchor' -import { SignerWalletAdapter } from '@solana/wallet-adapter-base' -import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { instantiateController, uxdClient } from './uxdClient' const createInitializeControllerInstruction = ( uxdProgramId: PublicKey, mintDecimals: number, authority: PublicKey, - payer: PublicKey, - connection: Connection, - wallet: SignerWalletAdapter + payer: PublicKey ): TransactionInstruction => { - const client = uxdClient(connection, uxdProgramId, wallet) + const client = uxdClient(uxdProgramId) return client.createInitializeControllerInstruction( instantiateController(uxdProgramId, mintDecimals), diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index 385d34c0c2..4bdb500a9e 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -1,7 +1,6 @@ import { serializeInstructionToBase64 } from '@solana/spl-governance' import { Provider } from '@project-serum/anchor' import { Token, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token' -import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { TOKEN_PROGRAM_ID } from '@utils/tokens' import { Controller, findATAAddrSync } from '@uxdprotocol/uxd-client' @@ -21,14 +20,9 @@ const createRegisterMangoDepositoryInstruction = async ( authority: PublicKey, payer: PublicKey, depositoryMintName: string, - insuranceMintName: string, - wallet: SignerWalletAdapter + insuranceMintName: string ): Promise => { - const mango = await initializeMango( - connection.current, - connection.cluster, - wallet - ) + const mango = await initializeMango(connection.current, connection.cluster) const depositoryMint = getDepositoryMintKey( connection.cluster, @@ -45,7 +39,7 @@ const createRegisterMangoDepositoryInstruction = async ( insuranceMint ) - const client = uxdClient(connection.current, uxdProgramId, wallet) + const client = uxdClient(uxdProgramId) const [authorityInsuranceATA] = findATAAddrSync(authority, insuranceMint) const createAuthorityInsuranceItx = Token.createAssociatedTokenAccountInstruction( ASSOCIATED_TOKEN_PROGRAM_ID, diff --git a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts index 33923699f1..48de4cfbf5 100644 --- a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts @@ -1,17 +1,14 @@ import { Provider } from '@project-serum/anchor' -import { SignerWalletAdapter } from '@solana/wallet-adapter-base' -import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { Controller } from '@uxdprotocol/uxd-client' import { getControllerPda, uxdClient } from './uxdClient' const createSetMangoDepositoriesRedeemableSoftCapInstruction = ( - connection: Connection, uxdProgramId: PublicKey, softCapUiAmount: number, - authority: PublicKey, - wallet: SignerWalletAdapter + authority: PublicKey ): TransactionInstruction => { - const client = uxdClient(connection, uxdProgramId, wallet) + const client = uxdClient(uxdProgramId) return client.createSetMangoDepositoriesRedeemableSoftCapInstruction( { pda: getControllerPda(uxdProgramId) } as Controller, diff --git a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts index 2eba0ded67..56da002fec 100644 --- a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts @@ -1,17 +1,14 @@ import { Provider } from '@project-serum/anchor' -import { SignerWalletAdapter } from '@solana/wallet-adapter-base' -import { TransactionInstruction, PublicKey, Connection } from '@solana/web3.js' +import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { Controller } from '@uxdprotocol/uxd-client' import { getControllerPda, uxdClient } from './uxdClient' const createSetRedeemableGlobalSupplyCapInstruction = ( - connection: Connection, uxdProgramId: PublicKey, supplyCapUiAmount: number, - authority: PublicKey, - wallet: SignerWalletAdapter + authority: PublicKey ): TransactionInstruction => { - const client = uxdClient(connection, uxdProgramId, wallet) + const client = uxdClient(uxdProgramId) return client.createSetRedeemableGlobalSupplyCapInstruction( { pda: getControllerPda(uxdProgramId) } as Controller, diff --git a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts index 349f191ad1..3a35240ed1 100644 --- a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts @@ -1,5 +1,4 @@ import { Provider } from '@project-serum/anchor' -import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { Controller } from '@uxdprotocol/uxd-client' import type { ConnectionContext } from 'utils/connection' @@ -18,16 +17,11 @@ const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( authority: PublicKey, depositoryMintName: string, insuranceMintName: string, - insuranceWithdrawnAmount: number, - wallet: SignerWalletAdapter + insuranceWithdrawnAmount: number ): Promise => { - const client = uxdClient(connection.current, uxdProgramId, wallet) + const client = uxdClient(uxdProgramId) console.log(depositoryMintName, insuranceMintName) - const mango = await initializeMango( - connection.current, - connection.cluster, - wallet - ) + const mango = await initializeMango(connection.current, connection.cluster) const depository = instantiateMangoDepository( uxdProgramId, diff --git a/tools/sdk/uxdProtocol/uxdClient.ts b/tools/sdk/uxdProtocol/uxdClient.ts index 8aa03afe72..7348e7c49b 100644 --- a/tools/sdk/uxdProtocol/uxdClient.ts +++ b/tools/sdk/uxdProtocol/uxdClient.ts @@ -11,7 +11,6 @@ import { UXD, UXDHelpers, } from '@uxdprotocol/uxd-client' -import uxdIdl from './uxdIdl' export const DEPOSITORY_MINTS = { devnet: { @@ -62,7 +61,6 @@ export const isDepositoryRegistered = async ( try { await uxdHelper.getMangoDepositoryAccount( new Provider(connection, wallet, Provider.defaultOptions()), - uxdProgram, instantiateMangoDepository( uxdProgram.programId, getDepositoryMintKey(cluster, collateralName), @@ -78,22 +76,15 @@ export const isDepositoryRegistered = async ( } } -export const uxdClient = ( - connection: Connection, - programId: PublicKey, - wallet: Wallet -): UXD => { - const provider = new Provider(connection, wallet, Provider.defaultOptions()) - return new UXD(new Program(uxdIdl, programId, provider)) +export const uxdClient = (programId: PublicKey): UXD => { + return new UXD(programId) } export const initializeMango = async ( connection: Connection, - cluster: EndpointTypes, - wallet: Wallet + cluster: EndpointTypes ) => { - const provider = new Provider(connection, wallet, Provider.defaultOptions()) - return createAndInitializeMango(provider, cluster) + return createAndInitializeMango(connection, cluster) } export const getControllerPda = (uxdProgramId: PublicKey): PublicKey => { @@ -131,34 +122,3 @@ export const instantiateMangoDepository = ( uxdProgramId ) } - -// import { Program, Provider } from '@project-serum/anchor' -// import Wallet from '@project-serum/sol-wallet-adapter' -// import { Connection } from '@solana/web3.js' -// import { -// createAndInitializeMango, -// UXD, -// UXDHelpers, -// } from '@uxdprotocol/uxd-client' -// import useWalletStore from 'stores/useWalletStore' -// import uxdIdl, { UXD_PROGRAM_ID } from './uxdIdl' - -// export const uxdHelpers = new UXDHelpers(); - -// export const uxdClient = (): UXD => { -// const connection = useWalletStore((s) => s.connection) -// const wallet = useWalletStore((s) => s.current) - -// // PROBABLY NOT OK to fix -// const provider = new Provider(connection.current, wallet, Provider.defaultOptions()); - -// const uxdProgram = new Program(uxdIdl, UXD_PROGRAM_ID, provider); -// return new UXD(new Program(uxdIdl, UXD_PROGRAM_ID, provider)); -// } - -// export const initializeMango = async () => { -// const connection = useWalletStore((s) => s.connection) -// const wallet = useWalletStore((s) => s.current) -// const provider = new Provider(connection.current, wallet, Provider.defaultOptions()) -// return createAndInitializeMango(provider, `mainnet`); -// } diff --git a/yarn.lock b/yarn.lock index d18978e987..d7f785aa6f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1134,6 +1134,26 @@ resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.9.3.tgz" integrity sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ== +"@project-serum/anchor@0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.20.0.tgz#547f5c0ff7e66809fa7118b2e3abd8087b5ec519" + integrity sha512-p1KOiqGBIbNsopMrSVoPwgxR1iPffsdjMNCOysahTPL9whX2CLX9HQCdopHjYaGl7+SdHRuXml6Wahk/wUmC8g== + dependencies: + "@project-serum/borsh" "^0.2.2" + "@solana/web3.js" "^1.17.0" + base64-js "^1.5.1" + bn.js "^5.1.2" + bs58 "^4.0.1" + buffer-layout "^1.2.2" + camelcase "^5.3.1" + crypto-hash "^1.3.0" + eventemitter3 "^4.0.7" + find "^0.3.0" + js-sha256 "^0.9.0" + pako "^2.0.3" + snake-case "^3.0.4" + toml "^3.0.0" + "@project-serum/anchor@^0.11.1": version "0.11.1" resolved "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.11.1.tgz" @@ -1843,12 +1863,14 @@ "@typescript-eslint/types" "5.7.0" eslint-visitor-keys "^3.0.0" -"@uxdprotocol/uxd-client@^3.6.0": - version "3.6.0" - resolved "https://npm.pkg.github.com/download/@uxdprotocol/uxd-client/3.6.0/84a0d5e222396364604ba7b2b99ba2ccd94acef316885c0738eeafcc11eed0bf#932810a3a2e20eb0e819de0f11a268696e98a650" - integrity sha512-2/1qeDNX9ZwbZ7QN10+UhXnH2xWEtHyx+UqmB/DzbcR3UMWZoiY6n2NN+wg1L7BURGfDlXalamaYAswJR7ShYw== +"@uxdprotocol/uxd-client@4.10.0": + version "4.10.0" + resolved "https://npm.pkg.github.com/download/@uxdprotocol/uxd-client/4.10.0/9d0a6f2286b67ca56fad70c4269cf96dd758525ffbfb7768f6b952ec9e9794f3#8b3fab7c620a662890dd00a311af3d0b4cbe3434" + integrity sha512-GKf/4fUkdbp67mUTB2kVysxL5EnYuwlkzmAV8YstLRdzPSUZfegwN27doPL+u4eXB3PAcidYzJBpvkg2VLdgwQ== dependencies: "@blockworks-foundation/mango-client" "3.2.15" + "@project-serum/anchor" "0.20.0" + camelcase "^5.3.1" "@yarnpkg/lockfile@^1.1.0": version "1.1.0" From b96096c8782c4b1fc7b1e9c829c9fbcba32c0cd3 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 14 Jan 2022 23:19:35 +0900 Subject: [PATCH 053/204] update UXD program name address --- components/instructions/programs/names.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/instructions/programs/names.ts b/components/instructions/programs/names.ts index f698761f88..8a93a3b711 100644 --- a/components/instructions/programs/names.ts +++ b/components/instructions/programs/names.ts @@ -22,7 +22,7 @@ export const PROGRAM_NAMES = { '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8': 'Raydium AMM Program', EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q: 'Raydium Staking Program', - UXDQDbkAeGMPR7gqDykDNu22D9DnYrKdvZhvNmMu6QX: 'UXDProtocol Program', + UXD8m9cvwk4RcSxnX2HZ9VudQCEeDH6fRnB4CAP57Dr: 'UXD Protocol Program', UXDBdZFw33TgoKPQK2sXDCLfNrdio2gUzLs1yHePfMj: 'UXD IDO Program', SysvarRent111111111111111111111111111111111: 'Sysvar: Rent', From a8eabb7ee62a5dce4e682d8891024ca1622b8637 Mon Sep 17 00:00:00 2001 From: CryptoSheik <93099369+0xCryptoSheik@users.noreply.github.com> Date: Fri, 14 Jan 2022 22:57:28 +0100 Subject: [PATCH 054/204] fix UXD website url --- public/realms/devnet.json | 2 +- public/realms/mainnet-beta.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/realms/devnet.json b/public/realms/devnet.json index f98120b573..a5bc94147a 100644 --- a/public/realms/devnet.json +++ b/public/realms/devnet.json @@ -103,7 +103,7 @@ "programId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", "realmId": "3eZ6p8VxnmncMgVYK1oMGb7rSiZPxoi6vtpDr3qKMEG3", "ogImage": "/realms/UXD/img/logo.png", - "website": "https://www.uxd.fi.com/", + "website": "https://uxd.fi", "twitter": "@UXDProtocol" }, { diff --git a/public/realms/mainnet-beta.json b/public/realms/mainnet-beta.json index 439215e1d4..005aa31136 100644 --- a/public/realms/mainnet-beta.json +++ b/public/realms/mainnet-beta.json @@ -142,7 +142,7 @@ "programId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", "realmId": "GBXLYo4ycRNfzuzYeudu6y2ng4afNeW14WcpM2E4JJSL", "ogImage": "/realms/UXD/img/logo.png", - "website": "https://www.uxd.fi.com/", + "website": "https://uxd.fi", "twitter": "@UXDProtocol" }, { From 25ba32b43ce29593f17cfe27d04df39229f8ffb5 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Tue, 18 Jan 2022 04:50:37 +0900 Subject: [PATCH 055/204] add raydium liquidity wip --- package.json | 5 +- .../instructions/raydium/AddLiquidity.ts | 139 ++++++++++++++++++ .../raydium/createAddLiquidityInstruction.ts | 27 ++++ tools/sdk/raydium/poolKeys.ts | 27 ++++ yarn.lock | 21 +++ 5 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.ts create mode 100644 tools/sdk/raydium/createAddLiquidityInstruction.ts create mode 100644 tools/sdk/raydium/poolKeys.ts diff --git a/package.json b/package.json index f285e96b20..938648eccb 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,13 @@ "@emotion/styled": "^11.3.0", "@headlessui/react": "^1.0.0", "@heroicons/react": "^1.0.1", - "@project-serum/anchor": "^0.19.0", "@metaplex/js": "^4.10.1", "@nfteyez/sol-rayz": "^0.8.0", + "@project-serum/anchor": "^0.19.0", "@project-serum/borsh": "^0.2.2", "@project-serum/common": "^0.0.1-beta.3", "@project-serum/sol-wallet-adapter": "^0.2.0", + "@raydium-io/raydium-sdk": "^1.0.1-beta.24", "@solana/spl-governance": "^0.0.13", "@solana/spl-token": "^0.1.3", "@solana/wallet-adapter-base": "^0.7.1", @@ -95,4 +96,4 @@ "preset": "emotion" } } -} \ No newline at end of file +} diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.ts b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.ts new file mode 100644 index 0000000000..9271929d16 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.ts @@ -0,0 +1,139 @@ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + ProgramAuthorityForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import useWalletStore from 'stores/useWalletStore' +import Input from '@components/inputs/Input' +import { debounce } from '@utils/debounce' +import GovernedAccountSelect from '../../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { validateInstruction } from '@utils/instructionTools' +import createSetProgramAuthorityInstruction from '@tools/sdk/bpfUpgradeableLoader/createSetProgramAuthorityInstruction' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, + GovernanceAccountType, +} from '@solana/spl-governance' + +const AddLiquidityRaydium = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { getGovernancesByAccountType } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountType( + GovernanceAccountType.ProgramGovernance + ).map((x) => { + return { + governance: x, + } + }) + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({ + governedAccount: undefined, + accountId: programId?.toString(), + destinationAuthority: '', + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + async function getInstruction(): Promise { + const isValid = await validateInstruction({ schema, form, setFormErrors }) + let serializedInstruction = '' + if ( + isValid && + programId && + form.governedAccount?.governance?.account && + wallet?.publicKey + ) { + const upgradeIx = await createSetProgramAuthorityInstruction( + form.governedAccount.governance.account.governedAccount, + form.governedAccount.governance.pubkey, + new PublicKey(form.destinationAuthority) + ) + serializedInstruction = serializeInstructionToBase64(upgradeIx) + } + const obj: UiInstruction = { + serializedInstruction: serializedInstruction, + isValid, + governance: form.governedAccount?.governance, + } + return obj + } + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + if (form.destinationAuthority) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.destinationAuthority]) + useEffect(() => { + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) + const schema = yup.object().shape({ + destinationAuthority: yup + .string() + .required('new authority address is required'), + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + handleSetForm({ + value: evt.target.value, + propertyName: 'destinationAuthority', + }) + } + error={formErrors['destinationAuthority']} + /> + + ) +} + +export default AddLiquidityRaydium diff --git a/tools/sdk/raydium/createAddLiquidityInstruction.ts b/tools/sdk/raydium/createAddLiquidityInstruction.ts new file mode 100644 index 0000000000..bc054c39a2 --- /dev/null +++ b/tools/sdk/raydium/createAddLiquidityInstruction.ts @@ -0,0 +1,27 @@ +import { Liquidity } from '@raydium-io/raydium-sdk' +import { TransactionInstruction, PublicKey } from '@solana/web3.js' +import { UXP_USDC_POOL_KEYS } from './poolKeys' + +export const createAddLiquidityInstruction = ( + baseTokenAccount: PublicKey, //TokenA we want to deposit (USDC or UXP) + quoteTokenAccount: PublicKey, //Value in TokenB of TokenA (UXP or USDC) + lpTokenAccount: PublicKey, + baseAmountIn: number, + quoteAmountIn: number, + owner: PublicKey +): TransactionInstruction => { + const itx = Liquidity.makeAddLiquidityInstruction({ + poolKeys: UXP_USDC_POOL_KEYS, + userKeys: { + baseTokenAccount, + quoteTokenAccount, + lpTokenAccount, + owner, + }, + baseAmountIn: 1_000_000, + quoteAmountIn: 1_000_000, + fixedSide: 'quote', + }) + + return itx +} diff --git a/tools/sdk/raydium/poolKeys.ts b/tools/sdk/raydium/poolKeys.ts new file mode 100644 index 0000000000..37be115a4c --- /dev/null +++ b/tools/sdk/raydium/poolKeys.ts @@ -0,0 +1,27 @@ +import { LiquidityPoolKeys, jsonInfo2PoolKeys } from '@raydium-io/raydium-sdk' + +// fetched from https://sdk.raydium.io/liquidity/mainnet.json +export const UXP_USDC_POOL_KEYS: LiquidityPoolKeys = jsonInfo2PoolKeys({ + id: '6tmFJbMk5yVHFcFy7X2K8RwHjKLr6KVFLYXpgpBNeAxB', + baseMint: 'UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M', + quoteMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + lpMint: 'AyuurXCCF2KdYTURbN3JsDKzZFZSiRqKc8UcZnrgBsGm', + version: 4, + programId: '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8', + authority: '5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1', + openOrders: '7AP5KPxkc9TYtYvqyXc4RK9GRVutGSne8Pj4ryKJoY4Z', + targetOrders: 'DfEhXNWDjsDNz1bqz6GinQU8RepjFneosamAM2XZ3heT', + baseVault: '3Dtb2kDA3pJkUrULXmQa8qn1RkmgnEM4eo2nf6Uuq3K3', + quoteVault: 'Gh2YaVC1sjzZQMixnHNXDin6awBAV6p2D5zY8STMu4p4', + withdrawQueue: '27BsfZSe59K2WXbhGGrvpySTRhd12moxxdLpYm6coFDT', + lpVault: '2dbkq546TV6C7Dmx5HWdHx7sTf6tpetvryqDRrcsE7kQ', + marketVersion: 3, + marketProgramId: '9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin', + marketId: '7KQpsp914VYnh62yV6AGfoG9hprfA14SgzEyqr6u9NY1', + marketAuthority: '5F4DUyyDR2uH7VTADLzi1CFmsVBVqPXk4TM4yHf9WDJi', + marketBaseVault: '9QGayBN3ycectkhLKiTPcfM9iFVtFpefSGWRr3XUoLwk', + marketQuoteVault: 'EiVf38NCvDFVJQqF5FgX1zeQ26Mzr88iELFugUSMJzu9', + marketBids: 'L6vnHnDLf8EPKXyaNAyhpkCdocvtkpNX8euVFZtqjCQ', + marketAsks: '68xmWKfE32qDoFL4iKBsjKpDyAfaiW4efdTDSAm33sKj', + marketEventQueue: 'Cta4TEwKCKhSphkMNzXsURVr2V6mozm2SPaV8tCgDPwy', +}) diff --git a/yarn.lock b/yarn.lock index 59abeb7ccd..8048bb1798 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1268,6 +1268,17 @@ bs58 "^4.0.1" eventemitter3 "^4.0.7" +"@raydium-io/raydium-sdk@^1.0.1-beta.24": + version "1.0.1-beta.24" + resolved "https://registry.yarnpkg.com/@raydium-io/raydium-sdk/-/raydium-sdk-1.0.1-beta.24.tgz#41633c2b893340a209f56287c896a50d48a5b5dc" + integrity sha512-29eR6IQYVhbnCuycA16lAinyXgvcdHyP4yV2QnG271HSPJcLhYgBL4flXdqlqY0yifigC3K8IThBtHTm1LtC3Q== + dependencies: + "@solana/buffer-layout" "^3.0.0" + "@solana/spl-token" "^0.1.8" + big.js "^6.1.1" + decimal.js-light "^2.5.1" + toformat "^2.0.0" + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" @@ -3337,6 +3348,11 @@ decamelize@^1.2.0: resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decimal.js-light@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + decimal.js@^10.2.1: version "10.2.1" resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz" @@ -9058,6 +9074,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toformat@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8" + integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ== + toidentifier@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz" From db4d6f980b59d0096c51a0fe44419fbb930ed6a8 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 20 Jan 2022 01:15:20 +0900 Subject: [PATCH 056/204] add Raydium Itx + Add Liquidity form - small refactor mint constants to get decimals --- components/instructions/tools.tsx | 3 + hooks/useGovernanceAssets.ts | 5 + .../instructions/SplTokenTransfer.tsx | 13 + .../instructions/raydium/AddLiquidity.ts | 139 ---------- .../instructions/raydium/AddLiquidity.tsx | 261 ++++++++++++++++++ pages/dao/[symbol]/proposal/new.tsx | 3 + .../raydium/createAddLiquidityInstruction.ts | 39 ++- tools/sdk/raydium/helpers.ts | 40 +++ tools/sdk/uxdProtocol/uxdClient.ts | 66 ++++- utils/uiTypes/proposalCreationTypes.ts | 9 + 10 files changed, 424 insertions(+), 154 deletions(-) delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.ts create mode 100644 pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx create mode 100644 tools/sdk/raydium/helpers.ts diff --git a/components/instructions/tools.tsx b/components/instructions/tools.tsx index 86229f9842..220523954e 100644 --- a/components/instructions/tools.tsx +++ b/components/instructions/tools.tsx @@ -85,6 +85,9 @@ export const ACCOUNT_NAMES = { // MonkOG DAO CVuCjHrqj97fSTsnSKzEBVPeYzXEEv6uiRjzBLRvnouj: 'MonkOG DAO Treasury Vault', + + //UXD DAO + '9SAveSCmGTVR9csAjK45keGitb1kdsC22Pb1AFdoUcSD': 'UXD DAO Treasury Vault', } // Blacklisted governances which should not be displayed in the UI diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 89b4a47a7c..38fef4251d 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -177,6 +177,11 @@ export default function useGovernanceAssets() { name: 'Withdraw Insurance From Mango Depository', isVisible: canUseUxdInstructions, }, + { + id: Instructions.AddLiquidityRaydium, + name: 'Add To Raydium Liquidity Pool', + isVisible: canUseUxdInstructions, + }, { id: Instructions.Transfer, name: 'Transfer Tokens', diff --git a/pages/dao/[symbol]/proposal/components/instructions/SplTokenTransfer.tsx b/pages/dao/[symbol]/proposal/components/instructions/SplTokenTransfer.tsx index b5847b7353..89c43d2a0c 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SplTokenTransfer.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SplTokenTransfer.tsx @@ -126,6 +126,19 @@ const SplTokenTransfer = ({ index ) }, [form]) + + console.log( + 'governance owner', + form.governedTokenAccount?.governance?.owner.toBase58() + ) + console.log( + 'governance pubkey', + form.governedTokenAccount?.governance?.pubkey.toBase58() + ) + console.log( + 'governance governed account', + form.governedTokenAccount?.governance?.account.governedAccount.toBase58() + ) useEffect(() => { setGovernedAccount(form.governedTokenAccount?.governance) setMintInfo(form.governedTokenAccount?.mint?.account) diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.ts b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.ts deleted file mode 100644 index 9271929d16..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.ts +++ /dev/null @@ -1,139 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' -import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - ProgramAuthorityForm, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' -import useWalletStore from 'stores/useWalletStore' -import Input from '@components/inputs/Input' -import { debounce } from '@utils/debounce' -import GovernedAccountSelect from '../../GovernedAccountSelect' -import { GovernedMultiTypeAccount } from '@utils/tokens' -import { validateInstruction } from '@utils/instructionTools' -import createSetProgramAuthorityInstruction from '@tools/sdk/bpfUpgradeableLoader/createSetProgramAuthorityInstruction' -import { - ProgramAccount, - serializeInstructionToBase64, - Governance, - GovernanceAccountType, -} from '@solana/spl-governance' - -const AddLiquidityRaydium = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { getGovernancesByAccountType } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountType( - GovernanceAccountType.ProgramGovernance - ).map((x) => { - return { - governance: x, - } - }) - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - accountId: programId?.toString(), - destinationAuthority: '', - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - async function getInstruction(): Promise { - const isValid = await validateInstruction({ schema, form, setFormErrors }) - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - wallet?.publicKey - ) { - const upgradeIx = await createSetProgramAuthorityInstruction( - form.governedAccount.governance.account.governedAccount, - form.governedAccount.governance.pubkey, - new PublicKey(form.destinationAuthority) - ) - serializedInstruction = serializeInstructionToBase64(upgradeIx) - } - const obj: UiInstruction = { - serializedInstruction: serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - if (form.destinationAuthority) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.destinationAuthority]) - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - const schema = yup.object().shape({ - destinationAuthority: yup - .string() - .required('new authority address is required'), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), - }) - - return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - - handleSetForm({ - value: evt.target.value, - propertyName: 'destinationAuthority', - }) - } - error={formErrors['destinationAuthority']} - /> - - ) -} - -export default AddLiquidityRaydium diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx new file mode 100644 index 0000000000..0e1f1d98bc --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx @@ -0,0 +1,261 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + AddLiquidityRaydiumForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import useWalletStore from 'stores/useWalletStore' +import Input from '@components/inputs/Input' +import { debounce } from '@utils/debounce' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import Select from '@components/inputs/Select' +import { + getGovernanceMintKey, + getGovernanceMintSymbols, +} from '@tools/sdk/uxdProtocol/uxdClient' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, + GovernanceAccountType, +} from '@solana/spl-governance' +import GovernedAccountSelect from '../../GovernedAccountSelect' +import { createAddLiquidityInstruction } from '@tools/sdk/raydium/createAddLiquidityInstruction' +import { getAmountOut } from '@tools/sdk/raydium/helpers' +import { UXP_USDC_POOL_KEYS } from '@tools/sdk/raydium/poolKeys' + +const AddLiquidityRaydium = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { getGovernancesByAccountType } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountType( + GovernanceAccountType.ProgramGovernance + ).map((x) => { + return { + governance: x, + } + }) + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({ + governedAccount: undefined, + baseTokenName: '', + quoteTokenName: '', + baseAmountIn: 0, + quoteAmountIn: 0, + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + async function getInstruction(): Promise { + const isValid = await validateInstruction() + let serializedInstruction = '' + if ( + isValid && + programId && + form.governedAccount?.governance?.account && + wallet?.publicKey + ) { + const createIx = createAddLiquidityInstruction( + getGovernanceMintKey(connection.cluster, form.baseTokenName), + getGovernanceMintKey(connection.cluster, form.quoteTokenName), + form.baseAmountIn, + form.quoteAmountIn, + form.governedAccount.governance.pubkey, + new PublicKey(wallet.publicKey.toBase58()) + ) + serializedInstruction = serializeInstructionToBase64(createIx) + } + const obj: UiInstruction = { + serializedInstruction, + isValid, + governance: form.governedAccount?.governance, + } + return obj + } + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + if (form.baseTokenName) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + // We are assuming for now that we only have one Liquidity Pool (UXP/USDC) + handleSetForm({ + value: getGovernanceMintSymbols(connection.cluster).filter( + (s) => s !== form.baseTokenName + )[0], + propertyName: 'quoteTokenName', + }) + } + }, [form.baseTokenName]) + + useEffect(() => { + if (form.quoteTokenName) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.quoteTokenName]) + + useEffect(() => { + if (form.baseAmountIn) { + debounce.debounceFcn(async () => { + handleSetForm({ + value: await getAmountOut( + UXP_USDC_POOL_KEYS, + form.baseTokenName, + form.baseAmountIn, + form.quoteTokenName, + connection + ), + propertyName: 'quoteAmountIn', + }) + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.baseAmountIn]) + + useEffect(() => { + if (form.quoteAmountIn) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.quoteAmountIn]) + + useEffect(() => { + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) + + const schema = yup.object().shape({ + baseTokenName: yup.string().required('Base Token Name is required'), + quoteTokenName: yup.string().required('Quote Token Name is required'), + baseAmountIn: yup + .number() + .moreThan(0, 'Amount for base token should be more than 0') + .required('Amount for base token is required'), + quoteAmountIn: yup + .number() + .moreThan(0, 'Amount for quote token should be more than 0') + .required('Amount for quote token is required'), + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + + + + + handleSetForm({ + value: evt.target.value, + propertyName: 'baseAmountIn', + }) + } + error={formErrors['baseAmountIn']} + /> + + + handleSetForm({ + value: evt.target.value, + propertyName: 'quoteAmountIn', + }) + } + disabled={true} + error={formErrors['quoteAmountIn']} + /> + + ) +} + +export default AddLiquidityRaydium diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index 6ec028e278..9bc72dcbec 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -46,6 +46,7 @@ import WithdrawInsuranceFromMangoDepository from './components/instructions/With import MakeChangeMaxAccounts from './components/instructions/Mango/MakeChangeMaxAccounts' import VoteBySwitch from './components/VoteBySwitch' import { getProgramVersionForRealm } from '@models/registry/api' +import AddLiquidityRaydium from './components/instructions/raydium/AddLiquidity' const schema = yup.object().shape({ title: yup.string().required('Title is required'), @@ -294,6 +295,8 @@ const New = () => { governance={governance} > ) + case Instructions.AddLiquidityRaydium: + return case Instructions.InitializeController: return case Instructions.SetRedeemableGlobalSupplyCap: diff --git a/tools/sdk/raydium/createAddLiquidityInstruction.ts b/tools/sdk/raydium/createAddLiquidityInstruction.ts index bc054c39a2..7f8340854d 100644 --- a/tools/sdk/raydium/createAddLiquidityInstruction.ts +++ b/tools/sdk/raydium/createAddLiquidityInstruction.ts @@ -1,25 +1,52 @@ -import { Liquidity } from '@raydium-io/raydium-sdk' +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + Liquidity, + TOKEN_PROGRAM_ID, +} from '@raydium-io/raydium-sdk' +import { serializeInstructionToBase64 } from '@solana/spl-governance' +import { Token } from '@solana/spl-token' import { TransactionInstruction, PublicKey } from '@solana/web3.js' +import { findATAAddrSync } from '@uxdprotocol/uxd-client' import { UXP_USDC_POOL_KEYS } from './poolKeys' export const createAddLiquidityInstruction = ( baseTokenAccount: PublicKey, //TokenA we want to deposit (USDC or UXP) quoteTokenAccount: PublicKey, //Value in TokenB of TokenA (UXP or USDC) - lpTokenAccount: PublicKey, baseAmountIn: number, quoteAmountIn: number, - owner: PublicKey + owner: PublicKey, + payer: PublicKey ): TransactionInstruction => { + const [lpTokenAccount] = findATAAddrSync(owner, UXP_USDC_POOL_KEYS.lpMint) + console.log( + 'lp token account to create if not exist: ', + lpTokenAccount.toBase58() + ) + + const createlpTokenAccountItx = Token.createAssociatedTokenAccountInstruction( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + UXP_USDC_POOL_KEYS.lpMint, + lpTokenAccount, + owner, // owner + payer // payer + ) + + console.log( + `Initialize Authority Insurance ATA (${lpTokenAccount.toBase58()}) itx:`, + serializeInstructionToBase64(createlpTokenAccountItx) + ) + const itx = Liquidity.makeAddLiquidityInstruction({ poolKeys: UXP_USDC_POOL_KEYS, userKeys: { baseTokenAccount, quoteTokenAccount, - lpTokenAccount, + lpTokenAccount: findATAAddrSync(owner, UXP_USDC_POOL_KEYS.lpMint)[0], owner, }, - baseAmountIn: 1_000_000, - quoteAmountIn: 1_000_000, + baseAmountIn, + quoteAmountIn, fixedSide: 'quote', }) diff --git a/tools/sdk/raydium/helpers.ts b/tools/sdk/raydium/helpers.ts new file mode 100644 index 0000000000..31a5711c78 --- /dev/null +++ b/tools/sdk/raydium/helpers.ts @@ -0,0 +1,40 @@ +import { + Liquidity, + LiquidityPoolKeys, + Percent, + Token, + TokenAmount, +} from '@raydium-io/raydium-sdk' +import { PublicKey } from '@solana/web3.js' +import { ConnectionContext } from '@utils/connection' +import { getGovernanceToken } from '../uxdProtocol/uxdClient' + +export const getAmountOut = async ( + poolKeys: LiquidityPoolKeys, + tokenNameIn: string, + amountIn: number, + tokenNameOut: string, + connection: ConnectionContext +) => { + const tokenInData = getGovernanceToken(connection.cluster, tokenNameIn) + const tokenOutData = getGovernanceToken(connection.cluster, tokenNameOut) + + const amountOut = Liquidity.computeCurrencyAmountOut({ + poolKeys, + poolInfo: await Liquidity.fetchInfo({ + connection: connection.current, + poolKeys, + }), + currencyAmountIn: new TokenAmount( + new Token(new PublicKey(tokenInData.address), tokenInData.decimals), + amountIn + ), + currencyOut: new Token( + new PublicKey(tokenOutData.address), + tokenOutData.decimals + ), + slippage: new Percent(1, 100), + }) + + return amountOut.amountOut +} diff --git a/tools/sdk/uxdProtocol/uxdClient.ts b/tools/sdk/uxdProtocol/uxdClient.ts index 7348e7c49b..2a0a8588bf 100644 --- a/tools/sdk/uxdProtocol/uxdClient.ts +++ b/tools/sdk/uxdProtocol/uxdClient.ts @@ -14,22 +14,56 @@ import { export const DEPOSITORY_MINTS = { devnet: { - BTC: '3UNBZ6o52WTWwjac2kPUb4FyodhU1vFkRJheu1Sh2TvU', - SOL: 'So11111111111111111111111111111111111111112', + BTC: { + address: '3UNBZ6o52WTWwjac2kPUb4FyodhU1vFkRJheu1Sh2TvU', + decimals: 6, + }, + SOL: { + address: 'So11111111111111111111111111111111111111112', + decimals: 9, + }, }, mainnet: { - BTC: '9n4nbM75f5Ui33ZbPYXn59EwSgE8CGsHtAeTH5YFeJ9E', - SOL: 'So11111111111111111111111111111111111111112', - MSOL: 'mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So', + BTC: { + address: '9n4nbM75f5Ui33ZbPYXn59EwSgE8CGsHtAeTH5YFeJ9E', + decimals: 6, + }, + SOL: { + address: 'So11111111111111111111111111111111111111112', + decimals: 9, + }, + MSOL: { + address: 'mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So', + decimals: 6, + }, }, } export const INSURANCE_MINTS = { devnet: { - USDC: '8FRFC6MoGGkMFQwngccyu69VnYbzykGeez7ignHVAFSN', + USDC: { + address: '8FRFC6MoGGkMFQwngccyu69VnYbzykGeez7ignHVAFSN', + decimals: 6, + }, }, mainnet: { - USDC: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + USDC: { + address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + decimals: 6, + }, + }, +} + +export const GOVERNANCE_MINTS = { + mainnet: { + USDC: { + address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + decimals: 6, + }, + UXP: { + address: 'UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M', + decimals: 9, + }, }, } @@ -39,7 +73,10 @@ export const getDepositoryMintSymbols = (cluster: Cluster): string[] => [ export const getDepositoryMintKey = ( cluster: Cluster, symbol: string -): PublicKey => new PublicKey(DEPOSITORY_MINTS[cluster][symbol]) +): PublicKey => new PublicKey(DEPOSITORY_MINTS[cluster][symbol].address) + +export const getDepositoryToken = (cluster: Cluster, symbol: string) => + DEPOSITORY_MINTS[cluster][symbol] export const getInsuranceMintSymbols = (cluster: Cluster): string[] => [ ...Object.keys(INSURANCE_MINTS[cluster]), @@ -47,7 +84,18 @@ export const getInsuranceMintSymbols = (cluster: Cluster): string[] => [ export const getInsuranceMintKey = ( cluster: Cluster, symbol: string -): PublicKey => new PublicKey(INSURANCE_MINTS[cluster][symbol]) +): PublicKey => new PublicKey(INSURANCE_MINTS[cluster][symbol].address) + +export const getGovernanceMintSymbols = (cluster: Cluster): string[] => [ + ...Object.keys(GOVERNANCE_MINTS[cluster]), +] +export const getGovernanceMintKey = ( + cluster: Cluster, + symbol: string +): PublicKey => new PublicKey(GOVERNANCE_MINTS[cluster][symbol].address) + +export const getGovernanceToken = (cluster: Cluster, symbol: string) => + GOVERNANCE_MINTS[cluster][symbol] export const isDepositoryRegistered = async ( connection: Connection, diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 073b23fb5c..310ef43fe2 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -49,6 +49,14 @@ export interface ProgramAuthorityForm { destinationAuthority: string } +export interface AddLiquidityRaydiumForm { + governedAccount: GovernedTokenAccount | undefined + baseTokenName: string + quoteTokenName: string + baseAmountIn: number + quoteAmountIn: number +} + export interface MangoMakeChangeMaxAccountsForm { governedAccount: GovernedProgramAccount | undefined programId: string | undefined @@ -73,6 +81,7 @@ export enum Instructions { Mint, Base64, None, + AddLiquidityRaydium, InitializeController, SetRedeemableGlobalSupplyCap, SetMangoDepositoriesRedeemableSoftCap, From d3554bff3337047ade6f26a3d87451558d8cdfe8 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sat, 22 Jan 2022 09:53:46 +0900 Subject: [PATCH 057/204] add liquidity to raydium proposal itx on any governance --- hooks/useGovernanceAssets.ts | 2 +- package.json | 6 +- .../instructions/raydium/AddLiquidity.tsx | 91 ++++++++++++++++--- tools/routing.ts | 11 ++- .../raydium/createAddLiquidityInstruction.ts | 67 ++++++++++++-- tools/sdk/raydium/helpers.ts | 8 +- utils/uiTypes/proposalCreationTypes.ts | 4 +- yarn.lock | 8 +- 8 files changed, 160 insertions(+), 37 deletions(-) diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 38fef4251d..ed2c85ce66 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -180,7 +180,7 @@ export default function useGovernanceAssets() { { id: Instructions.AddLiquidityRaydium, name: 'Add To Raydium Liquidity Pool', - isVisible: canUseUxdInstructions, + isVisible: canUseAnyInstruction, }, { id: Instructions.Transfer, diff --git a/package.json b/package.json index 938648eccb..cd3980befa 100644 --- a/package.json +++ b/package.json @@ -34,13 +34,13 @@ "@project-serum/borsh": "^0.2.2", "@project-serum/common": "^0.0.1-beta.3", "@project-serum/sol-wallet-adapter": "^0.2.0", - "@raydium-io/raydium-sdk": "^1.0.1-beta.24", + "@raydium-io/raydium-sdk": "^1.0.1-beta.26", "@solana/spl-governance": "^0.0.13", "@solana/spl-token": "^0.1.3", "@solana/wallet-adapter-base": "^0.7.1", "@solana/wallet-adapter-phantom": "^0.7.0", "@solana/wallet-adapter-sollet": "^0.8.0", - "@solana/web3.js": "^1.30.2", + "@solana/web3.js": "^1.31.0", "@tippyjs/react": "^4.2.5", "@uxdprotocol/uxd-client": "4.10.0", "axios": "^0.21.1", @@ -96,4 +96,4 @@ "preset": "emotion" } } -} +} \ No newline at end of file diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx index 0e1f1d98bc..78308d616c 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx @@ -16,19 +16,19 @@ import { debounce } from '@utils/debounce' import { GovernedMultiTypeAccount } from '@utils/tokens' import Select from '@components/inputs/Select' import { - getGovernanceMintKey, getGovernanceMintSymbols, + getGovernanceToken, } from '@tools/sdk/uxdProtocol/uxdClient' import { ProgramAccount, serializeInstructionToBase64, Governance, - GovernanceAccountType, } from '@solana/spl-governance' import GovernedAccountSelect from '../../GovernedAccountSelect' import { createAddLiquidityInstruction } from '@tools/sdk/raydium/createAddLiquidityInstruction' import { getAmountOut } from '@tools/sdk/raydium/helpers' import { UXP_USDC_POOL_KEYS } from '@tools/sdk/raydium/poolKeys' +import { BN } from '@project-serum/anchor' const AddLiquidityRaydium = ({ index, @@ -40,14 +40,47 @@ const AddLiquidityRaydium = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const { getGovernancesByAccountType } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountType( - GovernanceAccountType.ProgramGovernance - ).map((x) => { - return { - governance: x, + // const { getGovernancesByAccountType } = useGovernanceAssets() + const [governedAccounts, setGovernedAccounts] = useState< + GovernedMultiTypeAccount[] + >([]) + const { + governancesArray, + governedTokenAccounts, + getMintWithGovernances, + } = useGovernanceAssets() + // const governedProgramAccounts = getGovernancesByAccountType( + // GovernanceAccountType.ProgramGovernance + // ).map((x) => { + // return { + // governance: x, + // } + // }) + + useEffect(() => { + async function prepGovernances() { + const mintWithGovernances = await getMintWithGovernances() + const matchedGovernances = governancesArray.map((gov) => { + const governedTokenAccount = governedTokenAccounts.find( + (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() + ) + const mintGovernance = mintWithGovernances.find( + (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() + ) + if (governedTokenAccount) { + return governedTokenAccount as GovernedMultiTypeAccount + } + if (mintGovernance) { + return mintGovernance as GovernedMultiTypeAccount + } + return { + governance: gov, + } + }) + setGovernedAccounts(matchedGovernances) } - }) + prepGovernances() + }, []) const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({ @@ -56,6 +89,7 @@ const AddLiquidityRaydium = ({ quoteTokenName: '', baseAmountIn: 0, quoteAmountIn: 0, + fixedSide: 'base', }) const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) @@ -77,11 +111,21 @@ const AddLiquidityRaydium = ({ form.governedAccount?.governance?.account && wallet?.publicKey ) { + const baseToken = getGovernanceToken( + connection.cluster, + form.baseTokenName + ) + const quoteToken = getGovernanceToken( + connection.cluster, + form.quoteTokenName + ) + const createIx = createAddLiquidityInstruction( - getGovernanceMintKey(connection.cluster, form.baseTokenName), - getGovernanceMintKey(connection.cluster, form.quoteTokenName), - form.baseAmountIn, - form.quoteAmountIn, + new PublicKey(baseToken.address), + new PublicKey(quoteToken.address), + new BN(form.baseAmountIn * 10 ** baseToken.decimals), + new BN(form.quoteAmountIn * 10 ** quoteToken.decimals), + form.fixedSide, form.governedAccount.governance.pubkey, new PublicKey(wallet.publicKey.toBase58()) ) @@ -176,13 +220,17 @@ const AddLiquidityRaydium = ({ .object() .nullable() .required('Program governed account is required'), + fixedSide: yup + .string() + .equals(['base', 'quote']) + .required('Fixed Side is required'), }) return ( <> { handleSetForm({ value, propertyName: 'governedAccount' }) }} @@ -254,6 +302,21 @@ const AddLiquidityRaydium = ({ disabled={true} error={formErrors['quoteAmountIn']} /> + ) } diff --git a/tools/routing.ts b/tools/routing.ts index 2ad2cc1a05..ee98dd4aa2 100644 --- a/tools/routing.ts +++ b/tools/routing.ts @@ -1,7 +1,12 @@ import { RealmInfo } from 'models/registry/api' export function getRealmExplorerHost(realmInfo: RealmInfo | undefined) { - return realmInfo?.symbol === 'MNGO' - ? 'dao.mango.markets' - : 'realms-explorer.com' + switch (realmInfo?.symbol) { + case 'MNGO': + return 'dao.mango.markets' + case 'UXD': + return 'governance.uxd.fi' + default: + return 'realms-explorer.com' + } } diff --git a/tools/sdk/raydium/createAddLiquidityInstruction.ts b/tools/sdk/raydium/createAddLiquidityInstruction.ts index 7f8340854d..b8f8834893 100644 --- a/tools/sdk/raydium/createAddLiquidityInstruction.ts +++ b/tools/sdk/raydium/createAddLiquidityInstruction.ts @@ -1,4 +1,6 @@ +import { BN } from '@project-serum/anchor' import { + AmountSide, ASSOCIATED_TOKEN_PROGRAM_ID, Liquidity, TOKEN_PROGRAM_ID, @@ -10,10 +12,11 @@ import { findATAAddrSync } from '@uxdprotocol/uxd-client' import { UXP_USDC_POOL_KEYS } from './poolKeys' export const createAddLiquidityInstruction = ( - baseTokenAccount: PublicKey, //TokenA we want to deposit (USDC or UXP) - quoteTokenAccount: PublicKey, //Value in TokenB of TokenA (UXP or USDC) - baseAmountIn: number, - quoteAmountIn: number, + tokenMintA: PublicKey, //TokenA we want to deposit (USDC or UXP) + tokenMintB: PublicKey, //Value in TokenB of TokenA (UXP or USDC) + amountA: BN, + amountB: BN, + fixedSide: AmountSide, owner: PublicKey, payer: PublicKey ): TransactionInstruction => { @@ -33,21 +36,71 @@ export const createAddLiquidityInstruction = ( ) console.log( - `Initialize Authority Insurance ATA (${lpTokenAccount.toBase58()}) itx:`, + `Initialize Authority LP token ATA (${lpTokenAccount.toBase58()}) itx:`, serializeInstructionToBase64(createlpTokenAccountItx) ) + const [baseTokenAccount] = findATAAddrSync(owner, UXP_USDC_POOL_KEYS.baseMint) + console.log( + 'usdc token account to create if not exist: ', + baseTokenAccount.toBase58() + ) + + const createBaseTokenAccountItx = Token.createAssociatedTokenAccountInstruction( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + UXP_USDC_POOL_KEYS.quoteMint, + baseTokenAccount, + owner, // owner + payer // payer + ) + + console.log( + `Initialize Authority BASE token ATA (${baseTokenAccount.toBase58()}) itx:`, + serializeInstructionToBase64(createBaseTokenAccountItx) + ) + + const [quoteTokenAccount] = findATAAddrSync( + owner, + UXP_USDC_POOL_KEYS.quoteMint + ) + console.log( + 'usdc token account to create if not exist: ', + quoteTokenAccount.toBase58() + ) + + const createQuoteTokenAccountItx = Token.createAssociatedTokenAccountInstruction( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + UXP_USDC_POOL_KEYS.quoteMint, + quoteTokenAccount, + owner, // owner + payer // payer + ) + + console.log( + `Initialize Authority QUOTE token ATA (${quoteTokenAccount.toBase58()}) itx:`, + serializeInstructionToBase64(createQuoteTokenAccountItx) + ) + + let baseAmountIn = amountA + let quoteAmountIn = amountB + if (tokenMintA.equals(UXP_USDC_POOL_KEYS.quoteMint)) { + baseAmountIn = amountB + quoteAmountIn = amountA + } + const itx = Liquidity.makeAddLiquidityInstruction({ poolKeys: UXP_USDC_POOL_KEYS, userKeys: { baseTokenAccount, quoteTokenAccount, - lpTokenAccount: findATAAddrSync(owner, UXP_USDC_POOL_KEYS.lpMint)[0], + lpTokenAccount, owner, }, baseAmountIn, quoteAmountIn, - fixedSide: 'quote', + fixedSide, }) return itx diff --git a/tools/sdk/raydium/helpers.ts b/tools/sdk/raydium/helpers.ts index 31a5711c78..7ddcde0b2d 100644 --- a/tools/sdk/raydium/helpers.ts +++ b/tools/sdk/raydium/helpers.ts @@ -18,7 +18,6 @@ export const getAmountOut = async ( ) => { const tokenInData = getGovernanceToken(connection.cluster, tokenNameIn) const tokenOutData = getGovernanceToken(connection.cluster, tokenNameOut) - const amountOut = Liquidity.computeCurrencyAmountOut({ poolKeys, poolInfo: await Liquidity.fetchInfo({ @@ -27,14 +26,15 @@ export const getAmountOut = async ( }), currencyAmountIn: new TokenAmount( new Token(new PublicKey(tokenInData.address), tokenInData.decimals), - amountIn + Number(amountIn) * 10 ** tokenInData.decimals ), currencyOut: new Token( new PublicKey(tokenOutData.address), tokenOutData.decimals ), - slippage: new Percent(1, 100), + slippage: new Percent(5, 1000), }) + const currentPrice = amountOut.currentPrice.toFixed(tokenOutData.decimals) - return amountOut.amountOut + return Number(currentPrice) * amountIn } diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 310ef43fe2..2bc2e93ae1 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -1,3 +1,4 @@ +import { AmountSide } from '@raydium-io/raydium-sdk' import { Governance, InstructionData } from '@solana/spl-governance' import { ProgramAccount } from '@solana/spl-governance' import { RpcContext } from '@solana/spl-governance' @@ -50,11 +51,12 @@ export interface ProgramAuthorityForm { } export interface AddLiquidityRaydiumForm { - governedAccount: GovernedTokenAccount | undefined + governedAccount: GovernedMultiTypeAccount | undefined baseTokenName: string quoteTokenName: string baseAmountIn: number quoteAmountIn: number + fixedSide: AmountSide } export interface MangoMakeChangeMaxAccountsForm { diff --git a/yarn.lock b/yarn.lock index 8048bb1798..028cbe56be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1268,10 +1268,10 @@ bs58 "^4.0.1" eventemitter3 "^4.0.7" -"@raydium-io/raydium-sdk@^1.0.1-beta.24": - version "1.0.1-beta.24" - resolved "https://registry.yarnpkg.com/@raydium-io/raydium-sdk/-/raydium-sdk-1.0.1-beta.24.tgz#41633c2b893340a209f56287c896a50d48a5b5dc" - integrity sha512-29eR6IQYVhbnCuycA16lAinyXgvcdHyP4yV2QnG271HSPJcLhYgBL4flXdqlqY0yifigC3K8IThBtHTm1LtC3Q== +"@raydium-io/raydium-sdk@^1.0.1-beta.26": + version "1.0.1-beta.26" + resolved "https://registry.yarnpkg.com/@raydium-io/raydium-sdk/-/raydium-sdk-1.0.1-beta.26.tgz#acbc63089713eb6606709aa6db0f49d1ed50b7ea" + integrity sha512-p+nY3gX+iroCfLSu198+Wzc65Ls5ygmGNQK0Liy5+F2A0X+kEKvhikM9QXZvAWmxGAh72iPbRGLpWEAw+GsCww== dependencies: "@solana/buffer-layout" "^3.0.0" "@solana/spl-token" "^0.1.8" From 965c86f1545907eb2dd9448f3e18eba291ecb770 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sat, 22 Jan 2022 10:01:15 +0900 Subject: [PATCH 058/204] cleanup --- components/instructions/tools.tsx | 3 ++- package.json | 2 +- .../components/instructions/SplTokenTransfer.tsx | 12 ------------ 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/components/instructions/tools.tsx b/components/instructions/tools.tsx index 220523954e..ee005f7afc 100644 --- a/components/instructions/tools.tsx +++ b/components/instructions/tools.tsx @@ -87,7 +87,8 @@ export const ACCOUNT_NAMES = { CVuCjHrqj97fSTsnSKzEBVPeYzXEEv6uiRjzBLRvnouj: 'MonkOG DAO Treasury Vault', //UXD DAO - '9SAveSCmGTVR9csAjK45keGitb1kdsC22Pb1AFdoUcSD': 'UXD DAO Treasury Vault', + '9SAveSCmGTVR9csAjK45keGitb1kdsC22Pb1AFdoUcSD': 'UXD DAO USDC Treasury Vault', + GEkb8xU5DrPw4TW75BgoRbqUeuyKpsZ4Q2RHMX9M74W5: 'UXD DAO UXP Treasury Vault', } // Blacklisted governances which should not be displayed in the UI diff --git a/package.json b/package.json index cd3980befa..b611014af9 100644 --- a/package.json +++ b/package.json @@ -96,4 +96,4 @@ "preset": "emotion" } } -} \ No newline at end of file +} diff --git a/pages/dao/[symbol]/proposal/components/instructions/SplTokenTransfer.tsx b/pages/dao/[symbol]/proposal/components/instructions/SplTokenTransfer.tsx index 89c43d2a0c..41ceca3e66 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SplTokenTransfer.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SplTokenTransfer.tsx @@ -127,18 +127,6 @@ const SplTokenTransfer = ({ ) }, [form]) - console.log( - 'governance owner', - form.governedTokenAccount?.governance?.owner.toBase58() - ) - console.log( - 'governance pubkey', - form.governedTokenAccount?.governance?.pubkey.toBase58() - ) - console.log( - 'governance governed account', - form.governedTokenAccount?.governance?.account.governedAccount.toBase58() - ) useEffect(() => { setGovernedAccount(form.governedTokenAccount?.governance) setMintInfo(form.governedTokenAccount?.mint?.account) From 691ca5ce343436762ecfac1963822c21a43729f4 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Mon, 24 Jan 2022 00:07:57 +0900 Subject: [PATCH 059/204] fix(itxs): pass an actual instance of the controller instead of just the pda --- .../createDepositInsuranceToMangoDepositoryInstruction.ts | 5 ++--- .../uxdProtocol/createRegisterMangoDepositoryInstruction.ts | 6 +++--- ...reateSetMangoDepositoriesRedeemableSoftCapInstruction.ts | 6 ++---- .../createSetRedeemableGlobalSupplyCapInstruction.ts | 6 ++---- ...createWithdrawInsuranceFromMangoDepositoryInstruction.ts | 5 ++--- tools/sdk/uxdProtocol/uxdClient.ts | 3 +-- 6 files changed, 12 insertions(+), 19 deletions(-) diff --git a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts index 19a9a23604..62933842db 100644 --- a/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction.ts @@ -1,14 +1,13 @@ import { Provider } from '@project-serum/anchor' import { TransactionInstruction, PublicKey } from '@solana/web3.js' -import { Controller } from '@uxdprotocol/uxd-client' import type { ConnectionContext } from 'utils/connection' import { uxdClient, initializeMango, instantiateMangoDepository, - getControllerPda, getDepositoryMintKey, getInsuranceMintKey, + instantiateController, } from './uxdClient' const createDepositInsuranceToMangoDepositoryInstruction = async ( @@ -31,7 +30,7 @@ const createDepositInsuranceToMangoDepositoryInstruction = async ( return client.createDepositInsuranceToMangoDepositoryInstruction( insuranceDepositedAmount, - { pda: getControllerPda(uxdProgramId) } as Controller, + instantiateController(uxdProgramId), depository, mango, authority, diff --git a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts index 4bdb500a9e..7458b7a67e 100644 --- a/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction.ts @@ -3,13 +3,13 @@ import { Provider } from '@project-serum/anchor' import { Token, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { TOKEN_PROGRAM_ID } from '@utils/tokens' -import { Controller, findATAAddrSync } from '@uxdprotocol/uxd-client' +import { findATAAddrSync } from '@uxdprotocol/uxd-client' import type { ConnectionContext } from 'utils/connection' import { - getControllerPda, getDepositoryMintKey, getInsuranceMintKey, initializeMango, + instantiateController, instantiateMangoDepository, uxdClient, } from './uxdClient' @@ -56,7 +56,7 @@ const createRegisterMangoDepositoryInstruction = async ( ) return client.createRegisterMangoDepositoryInstruction( - { pda: getControllerPda(uxdProgramId) } as Controller, + instantiateController(uxdProgramId), depository, mango, authority, diff --git a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts index 48de4cfbf5..ca8f72a6cb 100644 --- a/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction.ts @@ -1,7 +1,6 @@ import { Provider } from '@project-serum/anchor' import { TransactionInstruction, PublicKey } from '@solana/web3.js' -import { Controller } from '@uxdprotocol/uxd-client' -import { getControllerPda, uxdClient } from './uxdClient' +import { instantiateController, uxdClient } from './uxdClient' const createSetMangoDepositoriesRedeemableSoftCapInstruction = ( uxdProgramId: PublicKey, @@ -9,9 +8,8 @@ const createSetMangoDepositoriesRedeemableSoftCapInstruction = ( authority: PublicKey ): TransactionInstruction => { const client = uxdClient(uxdProgramId) - return client.createSetMangoDepositoriesRedeemableSoftCapInstruction( - { pda: getControllerPda(uxdProgramId) } as Controller, + instantiateController(uxdProgramId), authority, softCapUiAmount, Provider.defaultOptions() diff --git a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts index 56da002fec..df2faf6322 100644 --- a/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts +++ b/tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction.ts @@ -1,7 +1,6 @@ import { Provider } from '@project-serum/anchor' import { TransactionInstruction, PublicKey } from '@solana/web3.js' -import { Controller } from '@uxdprotocol/uxd-client' -import { getControllerPda, uxdClient } from './uxdClient' +import { instantiateController, uxdClient } from './uxdClient' const createSetRedeemableGlobalSupplyCapInstruction = ( uxdProgramId: PublicKey, @@ -9,9 +8,8 @@ const createSetRedeemableGlobalSupplyCapInstruction = ( authority: PublicKey ): TransactionInstruction => { const client = uxdClient(uxdProgramId) - return client.createSetRedeemableGlobalSupplyCapInstruction( - { pda: getControllerPda(uxdProgramId) } as Controller, + instantiateController(uxdProgramId), authority, supplyCapUiAmount, Provider.defaultOptions() diff --git a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts index 3a35240ed1..eda212e010 100644 --- a/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts +++ b/tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction.ts @@ -1,14 +1,13 @@ import { Provider } from '@project-serum/anchor' import { TransactionInstruction, PublicKey } from '@solana/web3.js' -import { Controller } from '@uxdprotocol/uxd-client' import type { ConnectionContext } from 'utils/connection' import { uxdClient, initializeMango, instantiateMangoDepository, - getControllerPda, getDepositoryMintKey, getInsuranceMintKey, + instantiateController, } from './uxdClient' const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( @@ -31,7 +30,7 @@ const createWithdrawInsuranceFromMangoDepositoryInstruction = async ( return client.createWithdrawInsuranceFromMangoDepositoryInstruction( insuranceWithdrawnAmount, - { pda: getControllerPda(uxdProgramId) } as Controller, + instantiateController(uxdProgramId), depository, mango, authority, diff --git a/tools/sdk/uxdProtocol/uxdClient.ts b/tools/sdk/uxdProtocol/uxdClient.ts index 2a0a8588bf..597a7adcb1 100644 --- a/tools/sdk/uxdProtocol/uxdClient.ts +++ b/tools/sdk/uxdProtocol/uxdClient.ts @@ -139,10 +139,9 @@ export const getControllerPda = (uxdProgramId: PublicKey): PublicKey => { return findAddrSync([Buffer.from('CONTROLLER')], uxdProgramId)[0] } -// We do not need the mint symbol so it is just set with a placeholder value export const instantiateController = ( uxdProgramId: PublicKey, - mintDecimals: number, + mintDecimals = 6, mintSymbol = 'UXD' ) => { return new Controller(mintSymbol, mintDecimals, uxdProgramId) From 3ba167f42ed56d28abc901a74d0d277930340a6e Mon Sep 17 00:00:00 2001 From: Sebastian Bor Date: Sat, 22 Jan 2022 12:22:27 +0000 Subject: [PATCH 060/204] Update chat sdk to v2 (#272) --- actions/castVote.ts | 3 +++ actions/chat/postMessage.ts | 5 ++++- components/Members/MemberOverview.tsx | 3 ++- components/chat/DiscussionForm.tsx | 3 ++- package.json | 4 ++-- stores/useWalletStore.tsx | 8 +++++++- yarn.lock | 8 ++++---- 7 files changed, 24 insertions(+), 10 deletions(-) diff --git a/actions/castVote.ts b/actions/castVote.ts index 1432b529b2..a8f88c25eb 100644 --- a/actions/castVote.ts +++ b/actions/castVote.ts @@ -6,6 +6,7 @@ import { } from '@solana/web3.js' import { ChatMessageBody, + GOVERNANCE_CHAT_PROGRAM_ID, Proposal, withPostChatMessage, YesNoVote, @@ -51,7 +52,9 @@ export async function castVote( await withPostChatMessage( instructions, signers, + GOVERNANCE_CHAT_PROGRAM_ID, programId, + realm, proposal.account.governance, proposal.pubkey, tokeOwnerRecord, diff --git a/actions/chat/postMessage.ts b/actions/chat/postMessage.ts index 2321accc32..977b59685e 100644 --- a/actions/chat/postMessage.ts +++ b/actions/chat/postMessage.ts @@ -4,7 +4,7 @@ import { Transaction, TransactionInstruction, } from '@solana/web3.js' -import { Proposal } from '@solana/spl-governance' +import { GOVERNANCE_CHAT_PROGRAM_ID, Proposal } from '@solana/spl-governance' import { ChatMessageBody } from '@solana/spl-governance' import { withPostChatMessage } from '@solana/spl-governance' import { ProgramAccount } from '@solana/spl-governance' @@ -13,6 +13,7 @@ import { sendTransaction } from '../../utils/send' export async function postChatMessage( { connection, wallet, programId, walletPubkey }: RpcContext, + realm: PublicKey, proposal: ProgramAccount, tokeOwnerRecord: PublicKey, body: ChatMessageBody, @@ -27,7 +28,9 @@ export async function postChatMessage( await withPostChatMessage( instructions, signers, + GOVERNANCE_CHAT_PROGRAM_ID, programId, + realm, proposal.account.governance, proposal.pubkey, tokeOwnerRecord, diff --git a/components/Members/MemberOverview.tsx b/components/Members/MemberOverview.tsx index cfee8534bd..a110f04bde 100644 --- a/components/Members/MemberOverview.tsx +++ b/components/Members/MemberOverview.tsx @@ -11,7 +11,7 @@ import useQueryContext from '@hooks/useQueryContext' import useRealm from '@hooks/useRealm' import { getVoteRecordsByVoterMapByProposal } from '@models/api' import { isYesVote } from '@models/voteRecords' -import { VoteRecord } from '@solana/spl-governance' +import { GOVERNANCE_CHAT_PROGRAM_ID, VoteRecord } from '@solana/spl-governance' import { ChatMessage, ProgramAccount } from '@solana/spl-governance' import { getGovernanceChatMessagesByVoter } from '@solana/spl-governance' @@ -82,6 +82,7 @@ const MemberOverview = () => { ), getGovernanceChatMessagesByVoter( connection!.current, + GOVERNANCE_CHAT_PROGRAM_ID, new PublicKey(member!.walletAddress) ), ]) diff --git a/components/chat/DiscussionForm.tsx b/components/chat/DiscussionForm.tsx index cdcf79bbc7..c59ac44a3b 100644 --- a/components/chat/DiscussionForm.tsx +++ b/components/chat/DiscussionForm.tsx @@ -13,7 +13,7 @@ import { getProgramVersionForRealm } from '@models/registry/api' const DiscussionForm = () => { const [comment, setComment] = useState('') const connected = useWalletStore((s) => s.connected) - const { ownVoterWeight, realmInfo } = useRealm() + const { ownVoterWeight, realmInfo, realm } = useRealm() const [submitting, setSubmitting] = useState(false) @@ -41,6 +41,7 @@ const DiscussionForm = () => { try { await postChatMessage( rpcContext, + realm!.pubkey, proposal!, ownVoterWeight.getTokenRecord(), msg diff --git a/package.json b/package.json index f76dc78b2e..8bc1e40c48 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@project-serum/common": "^0.0.1-beta.3", "@project-serum/sol-wallet-adapter": "^0.2.0", "@raydium-io/raydium-sdk": "^1.0.1-beta.26", - "@solana/spl-governance": "^0.0.15", + "@solana/spl-governance": "^0.0.16", "@solana/spl-token": "^0.1.3", "@solana/wallet-adapter-base": "^0.7.1", "@solana/wallet-adapter-phantom": "^0.7.0", @@ -96,4 +96,4 @@ "preset": "emotion" } } -} +} \ No newline at end of file diff --git a/stores/useWalletStore.tsx b/stores/useWalletStore.tsx index 58259256f9..babdddeaec 100644 --- a/stores/useWalletStore.tsx +++ b/stores/useWalletStore.tsx @@ -18,6 +18,7 @@ import { getGovernanceAccounts, Governance, GovernanceAccountType, + GOVERNANCE_CHAT_PROGRAM_ID, Proposal, ProposalInstruction, Realm, @@ -428,7 +429,11 @@ const useWalletStore = create((set, get) => ({ getGovernanceAccounts(connection, programId, SignatoryRecord, [ pubkeyFilter(1, proposalPubKey)!, ]), - getGovernanceChatMessages(connection, proposalPubKey), + getGovernanceChatMessages( + connection, + GOVERNANCE_CHAT_PROGRAM_ID, + proposalPubKey + ), getGovernanceAccount( connection, proposal.account.tokenOwnerRecord, @@ -481,6 +486,7 @@ const useWalletStore = create((set, get) => ({ const chatMessages = await getGovernanceChatMessages( connection, + GOVERNANCE_CHAT_PROGRAM_ID, proposalPubKey ) diff --git a/yarn.lock b/yarn.lock index 3690405e0f..73ed4c4e91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1299,10 +1299,10 @@ dependencies: buffer "~6.0.3" -"@solana/spl-governance@^0.0.15": - version "0.0.15" - resolved "https://registry.yarnpkg.com/@solana/spl-governance/-/spl-governance-0.0.15.tgz#6f0bc1ab540e16decea0018cab5d73875b37fe9e" - integrity sha512-GrP+f4HsF+BJfh0Xav+okliAdoZv/aOPZvQgSe9eEW1BVOHo0rkHrGz235SYQq1Dsli4LnGxlKRSIhU2T+qouw== +"@solana/spl-governance@^0.0.16": + version "0.0.16" + resolved "https://registry.yarnpkg.com/@solana/spl-governance/-/spl-governance-0.0.16.tgz#9064f618278b5bc7304ff87ab7487c1aab80c81b" + integrity sha512-l1n5e/ztbV9AFR945TT0R7bSBJmKHp/RA2Zt5dim0RHpJwQu/rvl3q/5/grdt1MfcO6vxf9TJX7yiJMOuLvHjQ== dependencies: "@solana/web3.js" "^1.22.0" bignumber.js "^9.0.1" From 06a1c8fbdc00b12f4d13e55fa122c0e860f19625 Mon Sep 17 00:00:00 2001 From: Aaron Quirk Date: Sat, 22 Jan 2022 13:38:06 -0500 Subject: [PATCH 061/204] NFT 4 Cause DAO (#276) --- public/realms/NFT4CauseDAO/img/logo.png | Bin 0 -> 39898 bytes public/realms/mainnet-beta.json | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 public/realms/NFT4CauseDAO/img/logo.png diff --git a/public/realms/NFT4CauseDAO/img/logo.png b/public/realms/NFT4CauseDAO/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..437daa8ff504808ed6a3801766aa634529ff2ed4 GIT binary patch literal 39898 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 Date: Sun, 23 Jan 2022 09:13:16 -0800 Subject: [PATCH 062/204] Pass through rpc env variables (#275) * pass through rpc env variables * debug endpoints --- next.config.js | 2 ++ utils/connection.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/next.config.js b/next.config.js index 0db8feea09..fcb36851a4 100644 --- a/next.config.js +++ b/next.config.js @@ -25,6 +25,8 @@ module.exports = withTM({ }, env: { REALM: process.env.REALM, + MAINNET_RPC: process.env.MAINNET_RPC, + DEVNET_RPC: process.env.DEVNET_RPC, DEFAULT_GOVERNANCE_PROGRAM_ID: process.env.DEFAULT_GOVERNANCE_PROGRAM_ID, }, }) diff --git a/utils/connection.ts b/utils/connection.ts index f8eb062f5b..8e9d1b1841 100644 --- a/utils/connection.ts +++ b/utils/connection.ts @@ -19,6 +19,8 @@ const ENDPOINTS: EndpointInfo[] = [ }, ] +console.log('deployed ENDPOINTS:', ENDPOINTS) + export interface ConnectionContext { cluster: EndpointTypes current: Connection From a7e63ced89752e5da515bdb8247c8b899c656d45 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Mon, 24 Jan 2022 14:29:35 +0900 Subject: [PATCH 063/204] use genesysgo from env --- .env | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 4d5316a723..7064d7d76d 100644 --- a/.env +++ b/.env @@ -1,7 +1,6 @@ # Run in context of one realm: starts with default realm and disables realms page navigation REALM=UXD -MAINNET_RPC=https://billowing-proud-glitter.solana-mainnet.quiknode.pro/7f49d7f436c2d3af0738270d90dee86962f13a82/ +MAINNET_RPC=https://ssc-dao.genesysgo.net/ DEVNET_RPC=https://api.devnet.solana.com -UXD_PROGRAM_ID=UXDQDbkAeGMPR7gqDykDNu22D9DnYrKdvZhvNmMu6QX -GOVERNANCE_PROGRAM_ID=UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa + From 50fb1f64528ca5d6cc454e150dbf74afcafa7e73 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Mon, 24 Jan 2022 14:47:17 +0900 Subject: [PATCH 064/204] FIX prevent governenace from crashing if https://token-list.solana.com/solana.tokenlist.json returns undefined --- utils/services/token.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/services/token.tsx b/utils/services/token.tsx index 37f65fb874..89864ef6f3 100644 --- a/utils/services/token.tsx +++ b/utils/services/token.tsx @@ -25,7 +25,7 @@ class TokenService { const response = await axios.get( 'https://token-list.solana.com/solana.tokenlist.json' ) - this.tokenList = response.data.tokens + this.tokenList = response.data.tokens ?? [] } async fetchTokenPrices(mintAddresses: string[]) { if (!this.tokenList.length) { From cc9855639ab6ce99c8e9146a188bafcd636578aa Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Mon, 24 Jan 2022 14:47:50 +0900 Subject: [PATCH 065/204] persist this fucking last line in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8bc1e40c48..3eb408aa18 100644 --- a/package.json +++ b/package.json @@ -96,4 +96,4 @@ "preset": "emotion" } } -} \ No newline at end of file +} From 2c00237f4e04aa790c876a86ce4918bc7f01bf78 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Tue, 25 Jan 2022 00:15:06 +0900 Subject: [PATCH 066/204] improve handling of decimal numbers --- .../instructions/raydium/AddLiquidity.tsx | 122 ++++++++---------- tools/sdk/raydium/helpers.ts | 9 +- utils/uiTypes/proposalCreationTypes.ts | 8 +- 3 files changed, 66 insertions(+), 73 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx index 78308d616c..c220e78b27 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx @@ -85,10 +85,10 @@ const AddLiquidityRaydium = ({ const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({ governedAccount: undefined, - baseTokenName: '', - quoteTokenName: '', - baseAmountIn: 0, - quoteAmountIn: 0, + tokenAName: '', + tokenBName: '', + tokenAAmountIn: 0, + tokenBAmountIn: 0, fixedSide: 'base', }) const [formErrors, setFormErrors] = useState({}) @@ -111,20 +111,24 @@ const AddLiquidityRaydium = ({ form.governedAccount?.governance?.account && wallet?.publicKey ) { - const baseToken = getGovernanceToken( - connection.cluster, - form.baseTokenName - ) - const quoteToken = getGovernanceToken( - connection.cluster, - form.quoteTokenName - ) + const tokenA = getGovernanceToken(connection.cluster, form.tokenAName) + const tokenB = getGovernanceToken(connection.cluster, form.tokenBName) const createIx = createAddLiquidityInstruction( - new PublicKey(baseToken.address), - new PublicKey(quoteToken.address), - new BN(form.baseAmountIn * 10 ** baseToken.decimals), - new BN(form.quoteAmountIn * 10 ** quoteToken.decimals), + new PublicKey(tokenA.address), + new PublicKey(tokenB.address), + new BN( + ( + Number(Number(form.tokenAAmountIn).toFixed(tokenA.decimals)) * + 10 ** tokenA.decimals + ).toString() + ), + new BN( + ( + Number(Number(form.tokenBAmountIn).toFixed(tokenB.decimals)) * + 10 ** tokenB.decimals + ).toString() + ), form.fixedSide, form.governedAccount.governance.pubkey, new PublicKey(wallet.publicKey.toBase58()) @@ -146,7 +150,7 @@ const AddLiquidityRaydium = ({ }, [realmInfo?.programId]) useEffect(() => { - if (form.baseTokenName) { + if (form.tokenAName) { debounce.debounceFcn(async () => { const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) @@ -154,49 +158,31 @@ const AddLiquidityRaydium = ({ // We are assuming for now that we only have one Liquidity Pool (UXP/USDC) handleSetForm({ value: getGovernanceMintSymbols(connection.cluster).filter( - (s) => s !== form.baseTokenName + (s) => s !== form.tokenAName )[0], - propertyName: 'quoteTokenName', - }) - } - }, [form.baseTokenName]) - - useEffect(() => { - if (form.quoteTokenName) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) + propertyName: 'tokenBName', }) } - }, [form.quoteTokenName]) + }, [form.tokenAName]) useEffect(() => { - if (form.baseAmountIn) { + if (form.tokenAAmountIn) { debounce.debounceFcn(async () => { handleSetForm({ value: await getAmountOut( UXP_USDC_POOL_KEYS, - form.baseTokenName, - form.baseAmountIn, - form.quoteTokenName, + form.tokenAName, + Number(form.tokenAAmountIn), + form.tokenBName, connection ), - propertyName: 'quoteAmountIn', + propertyName: 'tokenBAmountIn', }) const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) }) } - }, [form.baseAmountIn]) - - useEffect(() => { - if (form.quoteAmountIn) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.quoteAmountIn]) + }, [form.tokenAAmountIn]) useEffect(() => { handleSetInstructions( @@ -206,16 +192,16 @@ const AddLiquidityRaydium = ({ }, [form]) const schema = yup.object().shape({ - baseTokenName: yup.string().required('Base Token Name is required'), - quoteTokenName: yup.string().required('Quote Token Name is required'), - baseAmountIn: yup + tokenAName: yup.string().required('Token A Name is required'), + tokenBName: yup.string().required('Token B Name is required'), + tokenAAmountIn: yup .number() - .moreThan(0, 'Amount for base token should be more than 0') - .required('Amount for base token is required'), - quoteAmountIn: yup + .moreThan(0, 'Amount for A token should be more than 0') + .required('Amount for A token is required'), + tokenBAmountIn: yup .number() - .moreThan(0, 'Amount for quote token should be more than 0') - .required('Amount for quote token is required'), + .moreThan(0, 'Amount for B token should be more than 0') + .required('Amount for B token is required'), governedAccount: yup .object() .nullable() @@ -240,11 +226,11 @@ const AddLiquidityRaydium = ({ governance={governance} > handleSetForm({ value: evt.target.value, - propertyName: 'baseAmountIn', + propertyName: 'tokenAAmountIn', }) } - error={formErrors['baseAmountIn']} + error={formErrors['tokenAAmountIn']} /> handleSetForm({ - value: evt.target.value, - propertyName: 'quoteAmountIn', + value: Number(evt.target.value), + propertyName: 'tokenBAmountIn', }) } disabled={true} - error={formErrors['quoteAmountIn']} + error={formErrors['tokenBAmountIn']} /> + handleSetForm({ value, propertyName: 'splTokenMintUIName' }) + } + error={formErrors['baseTokenName']} + > + {Object.entries(SPL_TOKENS).map(([key, { name, mint }]) => ( + +
+ {name} + + + {mint.toString()} + +
+
+ ))} + + + ) +} + +export default CreateAssociatedTokenAccount diff --git a/pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx b/pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx index 2bb3bfa2a6..a9b50dc1a9 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx @@ -24,11 +24,7 @@ const CustomBase64 = ({ governance: ProgramAccount | null }) => { const wallet = useWalletStore((s) => s.current) - const { - governancesArray, - governedTokenAccounts, - getMintWithGovernances, - } = useGovernanceAssets() + const { getGovernedMultiTypeAccounts } = useGovernanceAssets() const shouldBeGoverned = index !== 0 && governance const [governedAccounts, setGovernedAccounts] = useState< GovernedMultiTypeAccount[] @@ -44,30 +40,11 @@ const CustomBase64 = ({ setFormErrors({}) setForm({ ...form, [propertyName]: value }) } + useEffect(() => { - async function prepGovernances() { - const mintWithGovernances = await getMintWithGovernances() - const matchedGovernances = governancesArray.map((gov) => { - const governedTokenAccount = governedTokenAccounts.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() - ) - const mintGovernance = mintWithGovernances.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() - ) - if (governedTokenAccount) { - return governedTokenAccount as GovernedMultiTypeAccount - } - if (mintGovernance) { - return mintGovernance as GovernedMultiTypeAccount - } - return { - governance: gov, - } - }) - setGovernedAccounts(matchedGovernances) - } - prepGovernances() + getGovernedMultiTypeAccounts().then(setGovernedAccounts) }, []) + async function getInstruction(): Promise { const isValid = await validateInstruction({ schema, form, setFormErrors }) let serializedInstruction = '' diff --git a/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx b/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx index b4dcfe1bbe..f3eb12df04 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx @@ -19,11 +19,7 @@ const Empty = ({ index: number governance: ProgramAccount | null }) => { - const { - governancesArray, - governedTokenAccounts, - getMintWithGovernances, - } = useGovernanceAssets() + const { getGovernedMultiTypeAccounts } = useGovernanceAssets() const shouldBeGoverned = index !== 0 && governance const [governedAccounts, setGovernedAccounts] = useState< GovernedMultiTypeAccount[] @@ -37,30 +33,11 @@ const Empty = ({ setFormErrors({}) setForm({ ...form, [propertyName]: value }) } + useEffect(() => { - async function prepGovernances() { - const mintWithGovernances = await getMintWithGovernances() - const matchedGovernances = governancesArray.map((gov) => { - const governedTokenAccount = governedTokenAccounts.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() - ) - const mintGovernance = mintWithGovernances.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() - ) - if (governedTokenAccount) { - return governedTokenAccount as GovernedMultiTypeAccount - } - if (mintGovernance) { - return mintGovernance as GovernedMultiTypeAccount - } - return { - governance: gov, - } - }) - setGovernedAccounts(matchedGovernances) - } - prepGovernances() + getGovernedMultiTypeAccounts().then(setGovernedAccounts) }, []) + async function getInstruction(): Promise { const isValid = await validateInstruction({ schema, form, setFormErrors }) const obj: UiInstruction = { diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx new file mode 100644 index 0000000000..0dead0951d --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx @@ -0,0 +1,134 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + CreateSolendObligationAccountForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import useWalletStore from 'stores/useWalletStore' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, +} from '@solana/spl-governance' +import GovernedAccountSelect from '../../GovernedAccountSelect' +import { createObligationAccount } from '@tools/sdk/solend/createObligationAccount' + +const CreateObligationAccount = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const [governedAccounts, setGovernedAccounts] = useState< + GovernedMultiTypeAccount[] + >([]) + const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + useEffect(() => { + getGovernedMultiTypeAccounts().then(setGovernedAccounts) + }, []) + + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({}) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + + async function getInstruction(): Promise { + const isValid = await validateInstruction() + + if ( + !connection || + !isValid || + !programId || + !form.governedAccount?.governance?.account || + !wallet?.publicKey + ) { + return { + serializedInstruction: '', + isValid: false, + governance: form.governedAccount?.governance, + } + } + + const tx = await createObligationAccount({ + fundingAddress: wallet.publicKey, + walletAddress: form.governedAccount.governance.pubkey, + }) + + return { + serializedInstruction: serializeInstructionToBase64(tx), + isValid: true, + governance: form.governedAccount.governance, + } + } + + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + handleSetInstructions( + { + governedAccount: form.governedAccount?.governance, + getInstruction, + }, + index + ) + }, [form]) + + const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + ) +} + +export default CreateObligationAccount diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx new file mode 100644 index 0000000000..732c8c7862 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx @@ -0,0 +1,188 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + DepositReserveLiquidityAndObligationCollateralForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import useWalletStore from 'stores/useWalletStore' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, +} from '@solana/spl-governance' +import GovernedAccountSelect from '../../GovernedAccountSelect' +import Input from '@components/inputs/Input' +import { depositReserveLiquidityAndObligationCollateral } from '@tools/sdk/solend/depositReserveLiquidityAndObligationCollateral' +import Select from '@components/inputs/Select' + +import SolendConfiguration from '@tools/sdk/solend/configuration' + +import BigNumber from 'bignumber.js' +import { BN } from '@project-serum/anchor' + +const DepositReserveLiquidityAndObligationCollateral = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const [governedAccounts, setGovernedAccounts] = useState< + GovernedMultiTypeAccount[] + >([]) + const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + useEffect(() => { + getGovernedMultiTypeAccounts().then(setGovernedAccounts) + }, []) + + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [ + form, + setForm, + ] = useState({ + uiAmount: '0', + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + + async function getInstruction(): Promise { + const isValid = await validateInstruction() + + if ( + !connection || + !isValid || + !programId || + !form.governedAccount?.governance?.account || + !wallet?.publicKey || + !form.mintName + ) { + return { + serializedInstruction: '', + isValid: false, + governance: form.governedAccount?.governance, + } + } + + const tx = await depositReserveLiquidityAndObligationCollateral({ + obligationOwner: form.governedAccount.governance.pubkey, + liquidityAmount: new BN( + new BigNumber(form.uiAmount) + .shiftedBy( + SolendConfiguration.getSupportedMintInformation(form.mintName) + .decimals + ) + .toString() + ), + mintName: form.mintName, + }) + + return { + serializedInstruction: serializeInstructionToBase64(tx), + isValid: true, + governance: form.governedAccount.governance, + } + } + + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + handleSetInstructions( + { + governedAccount: form.governedAccount?.governance, + getInstruction, + }, + index + ) + }, [form]) + + const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), + uiAmount: yup + .number() + .moreThan(0, 'Amount should be more than 0') + .required('Amount is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + + + + handleSetForm({ + value: evt.target.value, + propertyName: 'uiAmount', + }) + } + error={formErrors['uiAmount']} + /> + + ) +} + +export default DepositReserveLiquidityAndObligationCollateral diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/InitObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/InitObligationAccount.tsx new file mode 100644 index 0000000000..6dd59f94b9 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/InitObligationAccount.tsx @@ -0,0 +1,134 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + InitSolendObligationAccountForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import useWalletStore from 'stores/useWalletStore' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, +} from '@solana/spl-governance' +import GovernedAccountSelect from '../../GovernedAccountSelect' +import { initObligationAccount } from '@tools/sdk/solend/initObligationAccount' + +const InitObligationAccount = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const [governedAccounts, setGovernedAccounts] = useState< + GovernedMultiTypeAccount[] + >([]) + + const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + useEffect(() => { + getGovernedMultiTypeAccounts().then(setGovernedAccounts) + }, []) + + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({}) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + + async function getInstruction(): Promise { + const isValid = await validateInstruction() + + if ( + !connection || + !isValid || + !programId || + !form.governedAccount?.governance?.account || + !wallet?.publicKey + ) { + return { + serializedInstruction: '', + isValid: false, + governance: form.governedAccount?.governance, + } + } + + const tx = await initObligationAccount({ + obligationOwner: form.governedAccount.governance.pubkey, + }) + + return { + serializedInstruction: serializeInstructionToBase64(tx), + isValid: true, + governance: form.governedAccount.governance, + } + } + + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + handleSetInstructions( + { + governedAccount: form.governedAccount?.governance, + getInstruction, + }, + index + ) + }, [form]) + + const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + ) +} + +export default InitObligationAccount diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshObligation.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshObligation.tsx new file mode 100644 index 0000000000..6c92890a3c --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshObligation.tsx @@ -0,0 +1,152 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + RefreshObligationForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import useWalletStore from 'stores/useWalletStore' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, +} from '@solana/spl-governance' +import GovernedAccountSelect from '../../GovernedAccountSelect' +import Select from '@components/inputs/Select' +import SolendConfiguration from '@tools/sdk/solend/configuration' +import { refreshObligation } from '@tools/sdk/solend/refreshObligation' + +const RefreshObligation = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const [governedAccounts, setGovernedAccounts] = useState< + GovernedMultiTypeAccount[] + >([]) + const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + useEffect(() => { + getGovernedMultiTypeAccounts().then(setGovernedAccounts) + }, []) + + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({}) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + + async function getInstruction(): Promise { + const isValid = await validateInstruction() + + if ( + !connection || + !isValid || + !programId || + !form.governedAccount?.governance?.account || + !form.mintName || + !wallet?.publicKey + ) { + return { + serializedInstruction: '', + isValid: false, + governance: form.governedAccount?.governance, + } + } + + const tx = await refreshObligation({ + obligationOwner: form.governedAccount.governance.pubkey, + mintNames: [form.mintName], + }) + + return { + serializedInstruction: serializeInstructionToBase64(tx), + isValid: true, + governance: form.governedAccount.governance, + } + } + + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + handleSetInstructions( + { + governedAccount: form.governedAccount?.governance, + getInstruction, + }, + index + ) + }, [form]) + + const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + + + ) +} + +export default RefreshObligation diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshReserve.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshReserve.tsx new file mode 100644 index 0000000000..fec9577787 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshReserve.tsx @@ -0,0 +1,111 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + RefreshReserveForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../../new' +import useWalletStore from 'stores/useWalletStore' +import { serializeInstructionToBase64 } from '@solana/spl-governance' +import Select from '@components/inputs/Select' +import SolendConfiguration from '@tools/sdk/solend/configuration' +import { refreshReserve } from '@tools/sdk/solend/refreshReserve' + +const RefreshReserve = ({ index }: { index: number }) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({}) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + + async function getInstruction(): Promise { + const isValid = await validateInstruction() + + if ( + !connection || + !isValid || + !programId || + !form.mintName || + !wallet?.publicKey + ) { + return { + serializedInstruction: '', + isValid: false, + governance: undefined, + } + } + + const tx = await refreshReserve({ + mintName: form.mintName, + }) + + return { + serializedInstruction: serializeInstructionToBase64(tx), + isValid: true, + governance: undefined, + } + } + + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + handleSetInstructions( + { + getInstruction, + }, + index + ) + }, [form]) + + const schema = yup.object().shape({ + mintName: yup.string().required('Token Name is required'), + }) + + return ( + <> + + + ) +} + +export default RefreshReserve diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx new file mode 100644 index 0000000000..4113fb0756 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx @@ -0,0 +1,187 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + WithdrawObligationCollateralAndRedeemReserveLiquidityForm, +} from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import useWalletStore from 'stores/useWalletStore' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, +} from '@solana/spl-governance' +import GovernedAccountSelect from '../../GovernedAccountSelect' +import Input from '@components/inputs/Input' +import Select from '@components/inputs/Select' +import SolendConfiguration from '@tools/sdk/solend/configuration' +import { withdrawObligationCollateralAndRedeemReserveLiquidity } from '@tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity' +import BigNumber from 'bignumber.js' +import { BN } from '@project-serum/anchor' + +const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const [governedAccounts, setGovernedAccounts] = useState< + GovernedMultiTypeAccount[] + >([]) + + const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + useEffect(() => { + getGovernedMultiTypeAccounts().then(setGovernedAccounts) + }, []) + + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [ + form, + setForm, + ] = useState({ + uiAmount: '0', + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + + async function getInstruction(): Promise { + const isValid = await validateInstruction() + + if ( + !connection || + !isValid || + !programId || + !form.governedAccount?.governance?.account || + !wallet?.publicKey || + !form.mintName + ) { + return { + serializedInstruction: '', + isValid: false, + governance: form.governedAccount?.governance, + } + } + + const tx = await withdrawObligationCollateralAndRedeemReserveLiquidity({ + obligationOwner: form.governedAccount.governance.pubkey, + liquidityAmount: new BN( + new BigNumber(form.uiAmount) + .shiftedBy( + SolendConfiguration.getSupportedMintInformation(form.mintName) + .decimals + ) + .toString() + ), + mintName: form.mintName, + }) + + return { + serializedInstruction: serializeInstructionToBase64(tx), + isValid: true, + governance: form.governedAccount.governance, + } + } + + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + handleSetInstructions( + { + governedAccount: form.governedAccount?.governance, + getInstruction, + }, + index + ) + }, [form]) + + const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), + uiAmount: yup + .number() + .moreThan(0, 'Amount should be more than 0') + .required('Amount is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + + + + handleSetForm({ + value: evt.target.value, + propertyName: 'uiAmount', + }) + } + error={formErrors['uiAmount']} + /> + + ) +} + +export default WithdrawObligationCollateralAndRedeemReserveLiquidity diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index b951c8929d..d03abb0f02 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -47,6 +47,13 @@ import TokenBalanceCardWrapper from '@components/TokenBalance/TokenBalanceCardWr import { getProgramVersionForRealm } from '@models/registry/api' import AddLiquidityRaydium from './components/instructions/raydium/AddLiquidity' import { useVoteRegistry } from 'VoteStakeRegistry/hooks/useVoteRegistry' +import CreateObligationAccount from './components/instructions/solend/CreateObligationAccount' +import InitObligationAccount from './components/instructions/solend/InitObligationAccount' +import DepositReserveLiquidityAndObligationCollateral from './components/instructions/solend/DepositReserveLiquidityAndObligationCollateral' +import WithdrawObligationCollateralAndRedeemReserveLiquidity from './components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity' +import CreateAssociatedTokenAccount from './components/instructions/CreateAssociatedTokenAccount' +import RefreshObligation from './components/instructions/solend/RefreshObligation' +import RefreshReserve from './components/instructions/solend/RefreshReserve' const schema = yup.object().shape({ title: yup.string().required('Title is required'), @@ -61,6 +68,23 @@ export const NewProposalContext = createContext( defaultGovernanceCtx ) +// Takes the first encountered governance account +function extractGovernanceAccountFromInstructionsData( + instructionsData: ComponentInstructionData[] +): ProgramAccount | null { + return instructionsData.reduce((tmp, x) => { + if (tmp) { + return tmp + } + + if (x.governedAccount) { + return x.governedAccount + } + + return tmp + }, null) +} + const New = () => { const router = useRouter() const { client } = useVoteRegistry() @@ -267,11 +291,13 @@ const New = () => { }, [instructionsData[0].governedAccount?.pubkey]) useEffect(() => { - const firstInstruction = instructionsData[0] - if (firstInstruction && firstInstruction.governedAccount) { - setGovernance(firstInstruction.governedAccount) - } - }, [instructionsData[0]]) + const governedAccount = extractGovernanceAccountFromInstructionsData( + instructionsData + ) + + setGovernance(governedAccount) + }, [instructionsData]) + useEffect(() => { //fetch to be up to date with amounts fetchTokenAccountsForSelectedRealmGovernances() @@ -297,6 +323,32 @@ const New = () => { governance={governance} > ) + case Instructions.CreateAssociatedTokenAccount: + return ( + + ) + case Instructions.CreateSolendObligationAccount: + return + case Instructions.InitSolendObligationAccount: + return + case Instructions.DepositReserveLiquidityAndObligationCollateral: + return ( + + ) + case Instructions.RefreshSolendObligation: + return + case Instructions.RefreshSolendReserve: + return + case Instructions.WithdrawObligationCollateralAndRedeemReserveLiquidity: + return ( + + ) case Instructions.AddLiquidityRaydium: return case Instructions.InitializeController: @@ -411,6 +463,7 @@ const New = () => { }} >

Instructions

+ {instructionsData.map((instruction, idx) => { const availableInstructionsForIdx = getAvailableInstructionsForIndex( idx diff --git a/tools/sdk/solend/configuration.ts b/tools/sdk/solend/configuration.ts new file mode 100644 index 0000000000..87441c4a9b --- /dev/null +++ b/tools/sdk/solend/configuration.ts @@ -0,0 +1,137 @@ +import { PublicKey } from '@solana/web3.js' + +// Describe what information should be provided for Solend +abstract class ASolendConfiguration { + abstract get programID(): PublicKey + abstract get lendingMarket(): PublicKey + abstract get lendingMarketAuthority() + abstract get createObligationConfiguration(): { + lamports: number + space: number + seed: string + } + + abstract getSupportedCollateralMintsInformation(): SupportedCollateralMintsInformation + abstract getSupportedMintInformation( + mint: SupportedMintName + ): SupportedMintInformation + abstract getReserveOfGivenMints(mintNames: SupportedMintName[]): PublicKey[] + abstract getSupportedMintNames(): SupportedMintName[] + abstract getTokenNameByReservePublicKey( + reserveToFind: PublicKey + ): string | undefined +} + +// Add here new token to support ... +export type SupportedMintName = 'USDC' +export type SupportedCollateralMintNames = 'cUSDC' + +type SupportedCollateralMintInformation = { + name: string + mint: PublicKey + decimals: number +} + +type SupportedCollateralMintsInformation = { + [key in SupportedCollateralMintNames]: SupportedCollateralMintInformation +} + +type SupportedMintInformation = { + mint: PublicKey + relatedCollateralMint: SupportedCollateralMintInformation + decimals: number + reserve: PublicKey + reserveLiquiditySupply: PublicKey + pythOracle: PublicKey + switchboardFeedAddress: PublicKey + reserveCollateralSupplySplTokenAccount: PublicKey +} + +type SupportedMintsInformation = { + [key in SupportedMintName]: SupportedMintInformation +} + +class SolendConfiguration implements ASolendConfiguration { + protected supportedCollateralMintsInformation: SupportedCollateralMintsInformation = { + cUSDC: { + name: 'Solend Protocol: cUSDC', + mint: new PublicKey('993dVFL2uXWYeoXuEBFXR4BijeXdTv4s6BzsCjJZuwqk'), + decimals: 6, + }, + } + + protected supportedMintsInformation: SupportedMintsInformation = { + USDC: { + relatedCollateralMint: this.supportedCollateralMintsInformation.cUSDC, + mint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), + decimals: 6, + reserve: new PublicKey('BgxfHJDzm44T7XG68MYKx7YisTjZu73tVovyZSjJMpmw'), + reserveLiquiditySupply: new PublicKey( + '8SheGtsopRUDzdiD6v6BR9a6bqZ9QwywYQY99Fp5meNf' + ), + pythOracle: new PublicKey('Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD'), + switchboardFeedAddress: new PublicKey( + 'CZx29wKMUxaJDq6aLVQTdViPL754tTR64NAgQBUGxxHb' + ), + reserveCollateralSupplySplTokenAccount: new PublicKey( + 'UtRy8gcEu9fCkDuUrU8EmC7Uc6FZy5NCwttzG7i6nkw' + ), + }, + } + + public get programID(): PublicKey { + return new PublicKey('So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo') + } + + public get lendingMarket(): PublicKey { + return new PublicKey('4UpD2fh7xH3VP9QQaXtsS1YY3bxzWhtfpks7FatyKvdY') + } + + public get lendingMarketAuthority(): PublicKey { + return new PublicKey('DdZR6zRFiUt4S5mg7AV1uKB2z1f1WzcNYCaTEEWPAuby') + } + + public get createObligationConfiguration() { + // All of theses numbers are magic numbers we got by looking at Solend documentation & transactions + return { + lamports: 9938880, + space: 1300, + seed: this.lendingMarket.toString().slice(0, 32), + } + } + + public getSupportedCollateralMintsInformation(): SupportedCollateralMintsInformation { + return this.supportedCollateralMintsInformation + } + + public getSupportedMintInformation( + mintName: SupportedMintName + ): SupportedMintInformation { + return this.supportedMintsInformation[mintName] + } + + public getReserveOfGivenMints(mintNames: SupportedMintName[]): PublicKey[] { + return mintNames.map((x) => this.supportedMintsInformation[x].reserve) + } + + public getSupportedMintNames(): SupportedMintName[] { + return Object.keys(this.supportedMintsInformation) as SupportedMintName[] + } + + public getTokenNameByReservePublicKey( + reserveToFind: PublicKey + ): string | undefined { + return Object.entries(this.supportedMintsInformation).reduce( + (tmp, [mintName, { reserve }]) => { + if (reserveToFind.toString() === reserve.toString()) { + return mintName + } + + return tmp + }, + void 0 + ) + } +} + +export default new SolendConfiguration() diff --git a/tools/sdk/solend/createObligationAccount.ts b/tools/sdk/solend/createObligationAccount.ts new file mode 100644 index 0000000000..d21d12780b --- /dev/null +++ b/tools/sdk/solend/createObligationAccount.ts @@ -0,0 +1,31 @@ +import { PublicKey, SystemProgram } from '@solana/web3.js' +import SolendConfiguration from './configuration' +import { deriveObligationAddressFromWalletAndSeed } from './utils' + +export async function createObligationAccount({ + fundingAddress, + walletAddress, +}: { + fundingAddress: PublicKey + walletAddress: PublicKey +}) { + const newAccountPubkey = await deriveObligationAddressFromWalletAndSeed( + walletAddress + ) + + const { + seed, + lamports, + space, + } = SolendConfiguration.createObligationConfiguration + + return SystemProgram.createAccountWithSeed({ + basePubkey: walletAddress, + fromPubkey: fundingAddress, + newAccountPubkey, + programId: SolendConfiguration.programID, + seed, + lamports, + space, + }) +} diff --git a/tools/sdk/solend/depositReserveLiquidityAndObligationCollateral.ts b/tools/sdk/solend/depositReserveLiquidityAndObligationCollateral.ts new file mode 100644 index 0000000000..504626d8f4 --- /dev/null +++ b/tools/sdk/solend/depositReserveLiquidityAndObligationCollateral.ts @@ -0,0 +1,83 @@ +import { BN } from '@project-serum/anchor' +import { PublicKey } from '@solana/web3.js' +import { depositReserveLiquidityAndObligationCollateralInstruction } from '@solendprotocol/solend-sdk' +import { findATAAddrSync } from '@uxdprotocol/uxd-client' +import { SupportedMintName } from './configuration' + +import SolendConfiguration from './configuration' + +import { deriveObligationAddressFromWalletAndSeed } from './utils' + +export async function depositReserveLiquidityAndObligationCollateral({ + obligationOwner, + liquidityAmount, + mintName, +}: { + obligationOwner: PublicKey + liquidityAmount: number | BN + mintName: SupportedMintName +}) { + const { + relatedCollateralMint, + mint, + reserve, + reserveLiquiditySupply, + pythOracle, + switchboardFeedAddress, + reserveCollateralSupplySplTokenAccount, + } = SolendConfiguration.getSupportedMintInformation(mintName) + + const reserveCollateralMint = relatedCollateralMint.mint + + const [usdcTokenAccount] = findATAAddrSync(obligationOwner, mint) + const [cusdcTokenAccount] = findATAAddrSync( + obligationOwner, + relatedCollateralMint.mint + ) + + const sourceLiquidity = usdcTokenAccount + const sourceCollateral = cusdcTokenAccount + const destinationCollateral = reserveCollateralSupplySplTokenAccount + + const obligation = await deriveObligationAddressFromWalletAndSeed( + obligationOwner + ) + + const transferAuthority = obligationOwner + + return depositReserveLiquidityAndObligationCollateralInstruction( + liquidityAmount, + + // Example: USDC token account address (owned by obligationOwner) + sourceLiquidity, + + // Destination Collateral Token Account + // Example: cUSDC's token account address (owned by obligationOwner) + sourceCollateral, + + // Solend Reserve Progam Id (must be related to sourceLiquidity) + // Complete list of reserves mint: https://docs.solend.fi/protocol/addresses + // Example: BgxfHJDzm44T7XG68MYKx7YisTjZu73tVovyZSjJMpmw for USDC reserve + reserve, + + // Solend Reserve SPL Token account address (must be related to sourceLiquidity) + // Example: 8SheGtsopRUDzdiD6v6BR9a6bqZ9QwywYQY99Fp5meNf for USDC (there are no list for it) + reserveLiquiditySupply, + + // Example: cUSDC mint (must be related to sourceLiquidity) + reserveCollateralMint, + + SolendConfiguration.lendingMarket, + SolendConfiguration.lendingMarketAuthority, + + destinationCollateral, + obligation, + obligationOwner, + pythOracle, + switchboardFeedAddress, + + // Wallet address of the one creating the proposal + transferAuthority, + SolendConfiguration.programID + ) +} diff --git a/tools/sdk/solend/initObligationAccount.ts b/tools/sdk/solend/initObligationAccount.ts new file mode 100644 index 0000000000..c957341aba --- /dev/null +++ b/tools/sdk/solend/initObligationAccount.ts @@ -0,0 +1,21 @@ +import { PublicKey, TransactionInstruction } from '@solana/web3.js' +import { initObligationInstruction } from '@solendprotocol/solend-sdk' +import SolendConfiguration from './configuration' +import { deriveObligationAddressFromWalletAndSeed } from './utils' + +export async function initObligationAccount({ + obligationOwner, +}: { + obligationOwner: PublicKey +}): Promise { + const obligationAddress = await deriveObligationAddressFromWalletAndSeed( + obligationOwner + ) + + return initObligationInstruction( + obligationAddress, + SolendConfiguration.lendingMarket, + obligationOwner, + SolendConfiguration.programID + ) +} diff --git a/tools/sdk/solend/refreshObligation.ts b/tools/sdk/solend/refreshObligation.ts new file mode 100644 index 0000000000..dea1229ebc --- /dev/null +++ b/tools/sdk/solend/refreshObligation.ts @@ -0,0 +1,31 @@ +import { PublicKey, TransactionInstruction } from '@solana/web3.js' +import { refreshObligationInstruction } from '@solendprotocol/solend-sdk' + +import SolendConfiguration, { SupportedMintName } from './configuration' + +import { deriveObligationAddressFromWalletAndSeed } from './utils' + +// Would be nice if we could automatically detect which reserves needs to be refreshed +// based on the obligationOwner assets in solend +export async function refreshObligation({ + obligationOwner, + mintNames, +}: { + obligationOwner: PublicKey + mintNames: SupportedMintName[] +}): Promise { + const obligationAddress = await deriveObligationAddressFromWalletAndSeed( + obligationOwner + ) + + const depositReserves = SolendConfiguration.getReserveOfGivenMints(mintNames) + + return refreshObligationInstruction( + obligationAddress, + // Both deposit reserves + borrow reserves parameters leads to the same data in instruction + // they are concatenate + depositReserves, + [], + SolendConfiguration.programID + ) +} diff --git a/tools/sdk/solend/refreshReserve.ts b/tools/sdk/solend/refreshReserve.ts new file mode 100644 index 0000000000..412868bc35 --- /dev/null +++ b/tools/sdk/solend/refreshReserve.ts @@ -0,0 +1,23 @@ +import { TransactionInstruction } from '@solana/web3.js' +import { refreshReserveInstruction } from '@solendprotocol/solend-sdk' + +import SolendConfiguration, { SupportedMintName } from './configuration' + +export async function refreshReserve({ + mintName, +}: { + mintName: SupportedMintName +}): Promise { + const { + reserve, + pythOracle, + switchboardFeedAddress, + } = SolendConfiguration.getSupportedMintInformation(mintName) + + return refreshReserveInstruction( + reserve, + SolendConfiguration.programID, + pythOracle, + switchboardFeedAddress + ) +} diff --git a/tools/sdk/solend/utils.ts b/tools/sdk/solend/utils.ts new file mode 100644 index 0000000000..3c60aa982b --- /dev/null +++ b/tools/sdk/solend/utils.ts @@ -0,0 +1,13 @@ +import { PublicKey } from '@solana/web3.js' + +import SolendConfiguration from './configuration' + +export async function deriveObligationAddressFromWalletAndSeed( + walletAddress: PublicKey +) { + return PublicKey.createWithSeed( + walletAddress, + SolendConfiguration.createObligationConfiguration.seed, + SolendConfiguration.programID + ) +} diff --git a/tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity.ts b/tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity.ts new file mode 100644 index 0000000000..36cdefe200 --- /dev/null +++ b/tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity.ts @@ -0,0 +1,59 @@ +import { BN } from '@project-serum/anchor' +import { PublicKey } from '@solana/web3.js' +import { withdrawObligationCollateralAndRedeemReserveLiquidity as originalWithdrawFunction } from '@solendprotocol/solend-sdk' +import { findATAAddrSync } from '@uxdprotocol/uxd-client' +import SolendConfiguration, { SupportedMintName } from './configuration' + +import { deriveObligationAddressFromWalletAndSeed } from './utils' + +export async function withdrawObligationCollateralAndRedeemReserveLiquidity({ + obligationOwner, + liquidityAmount, + mintName, +}: { + obligationOwner: PublicKey + liquidityAmount: number | BN + mintName: SupportedMintName +}) { + const { + relatedCollateralMint, + mint, + reserve, + reserveLiquiditySupply, + reserveCollateralSupplySplTokenAccount, + } = SolendConfiguration.getSupportedMintInformation(mintName) + + const reserveCollateralMint = relatedCollateralMint.mint + + const [usdcTokenAccount] = findATAAddrSync(obligationOwner, mint) + const [cusdcTokenAccount] = findATAAddrSync( + obligationOwner, + relatedCollateralMint.mint + ) + + const obligation = await deriveObligationAddressFromWalletAndSeed( + obligationOwner + ) + + const transferAuthority = obligationOwner + const sourceCollateral = reserveCollateralSupplySplTokenAccount + const destinationCollateral = cusdcTokenAccount + const withdrawReserve = reserve + const destinationLiquidity = usdcTokenAccount + + return originalWithdrawFunction( + liquidityAmount, + sourceCollateral, + destinationCollateral, + withdrawReserve, + obligation, + SolendConfiguration.lendingMarket, + SolendConfiguration.lendingMarketAuthority, + destinationLiquidity, + reserveCollateralMint, + reserveLiquiditySupply, + obligationOwner, + transferAuthority, + SolendConfiguration.programID + ) +} diff --git a/tools/sdk/units.ts b/tools/sdk/units.ts index 929503c12d..f35b4c60da 100644 --- a/tools/sdk/units.ts +++ b/tools/sdk/units.ts @@ -14,7 +14,6 @@ export function getTimestampFromDays(days: number) { /// Formats mint amount (natural units) as a decimal string export function fmtMintAmount(mint: MintInfo | undefined, mintAmount: BN) { - console.log('mint', mint) return mint ? getMintDecimalAmount(mint, mintAmount).toFormat() : new BigNumber(mintAmount.toString()).toFormat() diff --git a/utils/send.tsx b/utils/send.tsx index e90662dc3a..d2827f56e0 100644 --- a/utils/send.tsx +++ b/utils/send.tsx @@ -207,7 +207,7 @@ export async function sendSignedTransaction({ return txid } -async function awaitTransactionSignatureConfirmation( +export async function awaitTransactionSignatureConfirmation( txid: TransactionSignature, timeout: number, connection: Connection diff --git a/utils/splTokens.ts b/utils/splTokens.ts new file mode 100644 index 0000000000..cb201c9daf --- /dev/null +++ b/utils/splTokens.ts @@ -0,0 +1,51 @@ +import { PublicKey } from '@solana/web3.js' +import SolendConfiguration, { + SupportedCollateralMintNames as SolendSupportedCollateralMintNames, +} from '@tools/sdk/solend/configuration' + +export type SplTokenInformation = { + name: string + mint: PublicKey + decimals: number +} + +export type SupportedSplTokenNames = + | 'USDC' + | 'WSOL' + | SolendSupportedCollateralMintNames + +export const SPL_TOKENS: { + [key in SupportedSplTokenNames]: SplTokenInformation +} = { + USDC: { + name: 'USD Coin', + mint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), + decimals: 6, + }, + + WSOL: { + name: 'Wrapped SOL', + mint: new PublicKey('So11111111111111111111111111111111111111112'), + decimals: 9, + }, + + ...SolendConfiguration.getSupportedCollateralMintsInformation(), +} as const + +export type SplTokenUIName = typeof SPL_TOKENS[keyof typeof SPL_TOKENS]['name'] + +export function getSplTokenMintAddressByUIName( + nameToMatch: SplTokenUIName +): PublicKey { + const item = Object.entries(SPL_TOKENS).find( + ([_, { name }]) => name === nameToMatch + ) + + if (!item) { + throw new Error('must be here') + } + + const [, { mint }] = item + + return mint +} diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index a3f6ff1439..8d1fbc4115 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -4,6 +4,8 @@ import { ProgramAccount } from '@solana/spl-governance' import { RpcContext } from '@solana/spl-governance' import { MintInfo } from '@solana/spl-token' import { PublicKey, TransactionInstruction } from '@solana/web3.js' +import { SupportedMintName } from '@tools/sdk/solend/configuration' +import { SplTokenUIName } from '@utils/splTokens' import { GovernedMintInfoAccount, GovernedMultiTypeAccount, @@ -76,6 +78,41 @@ export interface EmptyInstructionForm { governedAccount: GovernedMultiTypeAccount | undefined } +export interface CreateAssociatedTokenAccountForm { + governedAccount?: GovernedMultiTypeAccount + splTokenMintUIName?: SplTokenUIName +} + +export interface CreateSolendObligationAccountForm { + governedAccount?: GovernedMultiTypeAccount +} + +export interface InitSolendObligationAccountForm { + governedAccount?: GovernedMultiTypeAccount +} + +export interface DepositReserveLiquidityAndObligationCollateralForm { + governedAccount?: GovernedMultiTypeAccount + uiAmount: string + mintName?: SupportedMintName +} + +export interface WithdrawObligationCollateralAndRedeemReserveLiquidityForm { + governedAccount?: GovernedMultiTypeAccount + uiAmount: string + mintName?: SupportedMintName +} + +export interface RefreshObligationForm { + governedAccount?: GovernedMultiTypeAccount + mintName?: SupportedMintName +} + +export interface RefreshReserveForm { + governedAccount?: GovernedMultiTypeAccount + mintName?: SupportedMintName +} + export enum Instructions { Transfer, ProgramUpgrade, @@ -91,6 +128,13 @@ export enum Instructions { DepositInsuranceToMangoDepository, WithdrawInsuranceFromMangoDepository, MangoMakeChangeMaxAccounts, + CreateAssociatedTokenAccount, + CreateSolendObligationAccount, + InitSolendObligationAccount, + DepositReserveLiquidityAndObligationCollateral, + WithdrawObligationCollateralAndRedeemReserveLiquidity, + RefreshSolendObligation, + RefreshSolendReserve, } export interface InitializeControllerForm { diff --git a/yarn.lock b/yarn.lock index 736c48e2c7..0ced9648bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1325,6 +1325,15 @@ bs58 "^4.0.1" eventemitter3 "^4.0.7" +"@pythnetwork/client@^2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.5.1.tgz#f15c1f2394157f4a97fa8725b93ad887d534e776" + integrity sha512-QXt31aWuVi02yvjmNEFDKurTkeTL/BEM4Ney4uixUbkxZDSrYRUuYNXPTrylRLdT3C3DlbF858/Jt7DwtLMR2w== + dependencies: + "@solana/web3.js" "^1.30.2" + assert "^2.0.0" + buffer "^6.0.1" + "@raydium-io/raydium-sdk@^1.0.1-beta.26": version "1.0.1-beta.26" resolved "https://registry.yarnpkg.com/@raydium-io/raydium-sdk/-/raydium-sdk-1.0.1-beta.26.tgz#acbc63089713eb6606709aa6db0f49d1ed50b7ea" @@ -1546,6 +1555,22 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" +"@solendprotocol/solend-sdk@^0.4.7": + version "0.4.7" + resolved "https://registry.yarnpkg.com/@solendprotocol/solend-sdk/-/solend-sdk-0.4.7.tgz#fd840994f71a4d16be1b1d35bbe8e446d150a7e5" + integrity sha512-jOoZr+B9QFbHNnKZ5Au05lj0+fEu81Eq+5dAxrTyDtG1TOiZgtAlyMO1jV3Uykj5FQrc9V8NA36E26lkHUXthA== + dependencies: + "@pythnetwork/client" "^2.5.1" + "@solana/buffer-layout" "^3.0.0" + "@solana/spl-token" "^0.1.8" + "@solana/web3.js" "^1.31.0" + axios "^0.24.0" + bignumber.js "^9.0.2" + bn.js "^5.2.0" + buffer "^6.0.3" + buffer-layout "^1.2.0" + isomorphic-fetch "^3.0.0" + "@testing-library/dom@^7.28.1": version "7.30.4" resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-7.30.4.tgz" @@ -2264,7 +2289,7 @@ asn1.js@^5.2.0: minimalistic-assert "^1.0.0" safer-buffer "^2.1.0" -assert@2.0.0: +assert@2.0.0, assert@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz" integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== @@ -2348,6 +2373,13 @@ axios@^0.21.1, axios@^0.21.4: dependencies: follow-redirects "^1.14.0" +axios@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== + dependencies: + follow-redirects "^1.14.4" + babel-jest@^27.4.5: version "27.4.5" resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.4.5.tgz" @@ -2469,6 +2501,11 @@ bignumber.js@^9.0.1: resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz" integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== +bignumber.js@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" + integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== + binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz" @@ -2754,7 +2791,7 @@ buffer@6.0.1: base64-js "^1.3.1" ieee754 "^1.2.1" -buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: +buffer@6.0.3, buffer@^6.0.1, buffer@^6.0.3, buffer@~6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -4374,6 +4411,11 @@ follow-redirects@^1.14.0: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz" integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== +follow-redirects@^1.14.4: + version "1.14.7" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" + integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" @@ -5421,6 +5463,14 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isomorphic-fetch@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" + integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== + dependencies: + node-fetch "^2.6.1" + whatwg-fetch "^3.4.1" + isomorphic-ws@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" @@ -9737,6 +9787,11 @@ whatwg-encoding@^1.0.5: dependencies: iconv-lite "0.4.24" +whatwg-fetch@^3.4.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" + integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== + whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" From 8581751d11df13591a3f59b4c9bad2f650b8bbf9 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 3 Feb 2022 22:28:27 +0900 Subject: [PATCH 083/204] update yarn files --- package.json | 2 +- yarn.lock | 94 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 5e8db38294..9c79152085 100644 --- a/package.json +++ b/package.json @@ -102,4 +102,4 @@ "preset": "emotion" } } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 7b80df0706..48258a3948 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1384,10 +1384,10 @@ resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.9.3.tgz" integrity sha512-xDu17cEfh7Kid/d95kB6tZsLOmSWKCZKtprnhVepjsSaCij+lM3mItSJDuuHDMbCWTh8Ejmebwb+KONcCJ0eXQ== -"@project-serum/anchor@0.20.1", "@project-serum/anchor@^0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.20.1.tgz#0937807e807e8332aa708cfef4bcb6cbb88b4129" - integrity sha512-2TuBmGUn9qeYz6sJINJlElrBuPsaUAtYyUsJ3XplEBf1pczrANAgs5ceJUFzdiqGEWLn+84ObSdBeChT/AXYFA== +"@project-serum/anchor@0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.20.0.tgz#547f5c0ff7e66809fa7118b2e3abd8087b5ec519" + integrity sha512-p1KOiqGBIbNsopMrSVoPwgxR1iPffsdjMNCOysahTPL9whX2CLX9HQCdopHjYaGl7+SdHRuXml6Wahk/wUmC8g== dependencies: "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" @@ -1404,10 +1404,10 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/anchor@^0.10.0": - version "0.10.0" - resolved "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.10.0.tgz" - integrity sha512-FWw3bQy1otINPq40uOTX0h8eE3drnespaq9Xkwpy3UXj6i46xuLjsWC+x9hp76EN4pC174POeNPvB0aivatswg== +"@project-serum/anchor@0.20.1", "@project-serum/anchor@^0.20.1": + version "0.20.1" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.20.1.tgz#0937807e807e8332aa708cfef4bcb6cbb88b4129" + integrity sha512-2TuBmGUn9qeYz6sJINJlElrBuPsaUAtYyUsJ3XplEBf1pczrANAgs5ceJUFzdiqGEWLn+84ObSdBeChT/AXYFA== dependencies: "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" @@ -1569,6 +1569,26 @@ bs58 "^4.0.1" eventemitter3 "^4.0.7" +"@pythnetwork/client@^2.5.1": + version "2.5.3" + resolved "https://registry.yarnpkg.com/@pythnetwork/client/-/client-2.5.3.tgz#86c9f92d01d8f282fdd8b5b11039da654e263988" + integrity sha512-NBLxPnA6A3tZb/DYUooD4SO63UJ70s9DzzFPGXcQNBR9itcycp7aaV+UA5oUPloD/4UHL9soo2fRuDVur0gmhA== + dependencies: + "@solana/web3.js" "^1.30.2" + assert "^2.0.0" + buffer "^6.0.1" + +"@raydium-io/raydium-sdk@^1.0.1-beta.26": + version "1.0.1-beta.26" + resolved "https://registry.yarnpkg.com/@raydium-io/raydium-sdk/-/raydium-sdk-1.0.1-beta.26.tgz#acbc63089713eb6606709aa6db0f49d1ed50b7ea" + integrity sha512-p+nY3gX+iroCfLSu198+Wzc65Ls5ygmGNQK0Liy5+F2A0X+kEKvhikM9QXZvAWmxGAh72iPbRGLpWEAw+GsCww== + dependencies: + "@solana/buffer-layout" "^3.0.0" + "@solana/spl-token" "^0.1.8" + big.js "^6.1.1" + decimal.js-light "^2.5.1" + toformat "^2.0.0" + "@reach/dialog@^0.16.2": version "0.16.2" resolved "https://registry.yarnpkg.com/@reach/dialog/-/dialog-0.16.2.tgz#567e6f59d0a6dabe84b2ba4c456404efa6cb7d03" @@ -1706,7 +1726,7 @@ integrity sha512-lR0EMP2HC3+Mxwd4YcnZb0smnaDw7Bl2IQWZiTevRH5ZZBZn6VRWn3/92E3qdU4SSImJkA6IDHawOHAnx/qUvQ== dependencies: buffer "~6.0.3" - + "@solana/spl-governance@^0.0.25": version "0.0.25" resolved "https://registry.yarnpkg.com/@solana/spl-governance/-/spl-governance-0.0.25.tgz#f951780fc0560df9fd9a7417bfc4fa76aa3fb2b1" @@ -1916,7 +1936,7 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.20.0", "@solana/web3.js@^1.21.0": +"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.20.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.22.0": version "1.32.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.32.0.tgz#b9821de52d0e773c363516c3dcef9be701295d82" integrity sha512-jquZ/VBvM3zXAaTJvdWd9mlP0WiZaZqjji0vw5UAsb5IKIossrLhHtyUqMfo41Qkdwu1aVwf7YWG748i4XIJnw== @@ -2818,6 +2838,13 @@ axios@^0.21.1, axios@^0.21.4: dependencies: follow-redirects "^1.14.0" +axios@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== + dependencies: + follow-redirects "^1.14.4" + axios@^0.25.0: version "0.25.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" @@ -3019,6 +3046,16 @@ bonjour@^3.5.0: multicast-dns "^6.0.1" multicast-dns-service-types "^1.1.0" +borsh@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.3.1.tgz#c31c3a149610e37913deada80e89073fb15cf55b" + integrity sha512-gJoSTnhwLxN/i2+15Y7uprU8h3CKI+Co4YKZKvrGYUy0FwHWM20x5Sx7eU8Xv4HQqV+7rb4r3P7K1cBIQe3q8A== + dependencies: + "@types/bn.js" "^4.11.5" + bn.js "^5.0.0" + bs58 "^4.0.0" + text-encoding-utf-8 "^1.0.2" + borsh@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.4.0.tgz#9dd6defe741627f1315eac2a73df61421f6ddb9f" @@ -3426,6 +3463,11 @@ chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + ci-info@^3.2.0: version "3.3.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz" @@ -4934,7 +4976,7 @@ follow-redirects@^1.14.0: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz" integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== -follow-redirects@^1.14.7: +follow-redirects@^1.14.4, follow-redirects@^1.14.7: version "1.14.7" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== @@ -4997,6 +5039,15 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" @@ -5698,6 +5749,13 @@ is-callable@^1.1.4, is-callable@^1.2.4: resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + is-color-stop@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" @@ -8403,6 +8461,11 @@ postcss@^8.3.5: picocolors "^1.0.0" source-map-js "^1.0.1" +postinstall-postinstall@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" + integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" @@ -9758,7 +9821,7 @@ superstruct@^0.14.2: resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b" integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ== -superstruct@^0.15.3: +superstruct@^0.15.2, superstruct@^0.15.3: version "0.15.3" resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.15.3.tgz#07edfc715259ebfe3b4b2e4cb53e8e45b51674a4" integrity sha512-wilec1Rg3FtKuRjRyCt70g5W29YUEuaLnybdVQUI+VQ7m0bw8k7TzrRv5iYmo6IpjLVrwxP5t3RgjAVqhYh4Fg== @@ -9960,6 +10023,13 @@ tippy.js@^6.3.1: dependencies: "@popperjs/core" "^2.8.3" +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmp@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" From f77b97f111ebaa991760a0d929d02855aeede9fc Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 3 Feb 2022 22:31:22 +0900 Subject: [PATCH 084/204] fix issues from merge --- hooks/useGovernanceAssets.ts | 5 ++++- utils/uiTypes/proposalCreationTypes.ts | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index cbde1d5769..f7c2c67c3b 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -101,7 +101,10 @@ export default function useGovernanceAssets() { const canUseUxdInstructions = symbol === 'UXP' && - canUseGovernanceForInstruction(GovernanceAccountType.ProgramGovernance) + canUseGovernanceForInstruction([ + GovernanceAccountType.ProgramGovernanceV1, + GovernanceAccountType.ProgramGovernanceV2, + ]) const canUseAnyInstruction = realm && diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 23b63202e4..3e14712be3 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -155,6 +155,8 @@ export enum Instructions { WithdrawObligationCollateralAndRedeemReserveLiquidity, RefreshSolendObligation, RefreshSolendReserve, + Grant, + Clawback, } export interface InitializeControllerForm { From 1f59f3aef91cf67ac364b120c43f0c6d5b15b78f Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 4 Feb 2022 23:27:46 +0900 Subject: [PATCH 085/204] update getGovernanceByAccountTypes call in uxd form itxs --- .../instructions/DepositInsuranceToMangoDepository.tsx | 9 +++++---- .../components/instructions/InitializeController.tsx | 9 +++++---- .../components/instructions/RegisterMangoDepository.tsx | 9 +++++---- .../SetMangoDepositoriesRedeemableSoftCap.tsx | 9 +++++---- .../components/instructions/SetProgramAuthority.tsx | 2 +- .../components/instructions/SetRedeemGlobalSupplyCap.tsx | 9 +++++---- .../WithdrawInsuranceFromMangoDepository.tsx | 9 +++++---- 7 files changed, 31 insertions(+), 25 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx index 873fe5ed2a..3851563b9c 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx @@ -38,10 +38,11 @@ const DepositInsuranceToMangoDepository = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const { getGovernancesByAccountType } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountType( - GovernanceAccountType.ProgramGovernance - ).map((x) => { + const { getGovernancesByAccountTypes } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountTypes([ + GovernanceAccountType.ProgramGovernanceV1, + GovernanceAccountType.ProgramGovernanceV2, + ]).map((x) => { return { governance: x, } diff --git a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx index cf7198d0c8..53b5373122 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx @@ -31,10 +31,11 @@ const InitializeController = ({ }) => { const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const { getGovernancesByAccountType } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountType( - GovernanceAccountType.ProgramGovernance - ).map((x) => { + const { getGovernancesByAccountTypes } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountTypes([ + GovernanceAccountType.ProgramGovernanceV1, + GovernanceAccountType.ProgramGovernanceV2, + ]).map((x) => { return { governance: x, } diff --git a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx index d648b2c9f6..7263fc851f 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx @@ -37,10 +37,11 @@ const RegisterMangoDepository = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const { getGovernancesByAccountType } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountType( - GovernanceAccountType.ProgramGovernance - ).map((x) => { + const { getGovernancesByAccountTypes } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountTypes([ + GovernanceAccountType.ProgramGovernanceV1, + GovernanceAccountType.ProgramGovernanceV2, + ]).map((x) => { return { governance: x, } diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx index b6f903540c..c89bfbafcc 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -32,10 +32,11 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ }) => { const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const { getGovernancesByAccountType } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountType( - GovernanceAccountType.ProgramGovernance - ).map((x) => { + const { getGovernancesByAccountTypes } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountTypes([ + GovernanceAccountType.ProgramGovernanceV1, + GovernanceAccountType.ProgramGovernanceV2, + ]).map((x) => { return { governance: x, } diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx index 0dfb73a887..295df70717 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx @@ -34,7 +34,7 @@ const SetProgramAuthority = ({ const { realmInfo } = useRealm() const { getGovernancesByAccountType } = useGovernanceAssets() const governedProgramAccounts = getGovernancesByAccountType( - GovernanceAccountType.ProgramGovernance + GovernanceAccountType.ProgramGovernanceV1 ).map((x) => { return { governance: x, diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx index 6d322e0102..87cd49f6e0 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx @@ -32,10 +32,11 @@ const SetRedeemGlobalSupplyCap = ({ }) => { const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const { getGovernancesByAccountType } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountType( - GovernanceAccountType.ProgramGovernance - ).map((x) => { + const { getGovernancesByAccountTypes } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountTypes([ + GovernanceAccountType.ProgramGovernanceV1, + GovernanceAccountType.ProgramGovernanceV2, + ]).map((x) => { return { governance: x, } diff --git a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx index e8023f289d..b08a8dad69 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx @@ -38,10 +38,11 @@ const WithdrawInsuranceFromMangoDepository = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const { getGovernancesByAccountType } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountType( - GovernanceAccountType.ProgramGovernance - ).map((x) => { + const { getGovernancesByAccountTypes } = useGovernanceAssets() + const governedProgramAccounts = getGovernancesByAccountTypes([ + GovernanceAccountType.ProgramGovernanceV1, + GovernanceAccountType.ProgramGovernanceV2, + ]).map((x) => { return { governance: x, } From 8b0e21ed541d1c26b393eeda2e771635ee13076c Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sat, 5 Feb 2022 00:01:31 +0900 Subject: [PATCH 086/204] fixes from spl-governance package update --- actions/executeInstructions.ts | 15 ++++++++------- .../instructions/ExecuteAllInstructionButton.tsx | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/actions/executeInstructions.ts b/actions/executeInstructions.ts index 6c515de535..a946d5c3dc 100644 --- a/actions/executeInstructions.ts +++ b/actions/executeInstructions.ts @@ -1,6 +1,6 @@ -import { Proposal, ProposalInstruction } from '@solana/spl-governance' +import { Proposal, ProposalTransaction } from '@solana/spl-governance' -import { withExecuteInstruction } from '@solana/spl-governance' +import { withExecuteTransaction } from '@solana/spl-governance' import { RpcContext } from '@solana/spl-governance' import { ProgramAccount } from '@solana/spl-governance' import { sendSignedTransaction, signTransaction } from '@utils/send' @@ -12,22 +12,23 @@ const DEFAULT_TIMEOUT = 31_000 // Merge instructions within one Transaction, sign it and execute it export const executeInstructions = async ( - { connection, wallet, programId }: RpcContext, + { connection, wallet, programId, programVersion }: RpcContext, proposal: ProgramAccount, - proposalInstructions: ProgramAccount[] + proposalInstructions: ProgramAccount[] ) => { const instructions: TransactionInstruction[] = [] await Promise.all( proposalInstructions.map((instruction) => - // withExecuteInstruction function mutate the given 'instructions' parameter - withExecuteInstruction( + // withExecuteTransaction function mutate the given 'instructions' parameter + withExecuteTransaction( instructions, programId, + programVersion, proposal.account.governance, proposal.pubkey, instruction.pubkey, - instruction.account.instruction + [instruction.account.getSingleInstruction()] ) ) ) diff --git a/components/instructions/ExecuteAllInstructionButton.tsx b/components/instructions/ExecuteAllInstructionButton.tsx index 3a4e4b145e..aeef6f9fc3 100644 --- a/components/instructions/ExecuteAllInstructionButton.tsx +++ b/components/instructions/ExecuteAllInstructionButton.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import { InstructionExecutionStatus, Proposal, - ProposalInstruction, + ProposalTransaction, ProposalState, } from '@solana/spl-governance' import React from 'react' @@ -31,7 +31,7 @@ export function ExecuteAllInstructionButton({ proposalInstructions, }: { proposal: ProgramAccount - proposalInstructions: ProgramAccount[] + proposalInstructions: ProgramAccount[] playing: PlayState setPlaying: React.Dispatch> }) { From bfc37bf4b868b613a757168936dd837736bd2fcf Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Mon, 24 Jan 2022 14:32:37 +0900 Subject: [PATCH 087/204] wip --- components/instructions/tools.tsx | 1 + .../instructions/raydium/RemoveLiquidity.tsx | 308 ++++++++++++++++++ .../createRemoveLiquidityInstruction.ts | 32 ++ tools/sdk/raydium/helpers.ts | 23 +- utils/uiTypes/proposalCreationTypes.ts | 5 + 5 files changed, 367 insertions(+), 2 deletions(-) create mode 100644 pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx create mode 100644 tools/sdk/raydium/createRemoveLiquidityInstruction.ts diff --git a/components/instructions/tools.tsx b/components/instructions/tools.tsx index f7481e4988..bb556c4682 100644 --- a/components/instructions/tools.tsx +++ b/components/instructions/tools.tsx @@ -92,6 +92,7 @@ export const ACCOUNT_NAMES = { //UXD DAO '9SAveSCmGTVR9csAjK45keGitb1kdsC22Pb1AFdoUcSD': 'UXD DAO USDC Treasury Vault', GEkb8xU5DrPw4TW75BgoRbqUeuyKpsZ4Q2RHMX9M74W5: 'UXD DAO UXP Treasury Vault', + '39qcqpaiLivyFLBLh4wvPmLrs336BBpXkKYb88i8dSiJ': 'UXD Council Token', } // Blacklisted governances which should not be displayed in the UI diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx new file mode 100644 index 0000000000..2634d0a89d --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx @@ -0,0 +1,308 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import useRealm from '@hooks/useRealm' +import { PublicKey } from '@solana/web3.js' +import * as yup from 'yup' +import { isFormValid } from '@utils/formValidation' +import { UiInstruction } from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from '../../../new' +import useGovernanceAssets from '@hooks/useGovernanceAssets' +import useWalletStore from 'stores/useWalletStore' +import Input from '@components/inputs/Input' +import { debounce } from '@utils/debounce' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import Select from '@components/inputs/Select' +import { + getGovernanceMintSymbols, + getGovernanceToken, +} from '@tools/sdk/uxdProtocol/uxdClient' +import { + ProgramAccount, + serializeInstructionToBase64, + Governance, +} from '@solana/spl-governance' +import GovernedAccountSelect from '../../GovernedAccountSelect' +import { getAmountOut } from '@tools/sdk/raydium/helpers' +import { UXP_USDC_POOL_KEYS } from '@tools/sdk/raydium/poolKeys' +import { BN } from '@project-serum/anchor' +import { createRemoveLiquidityInstruction } from '@tools/sdk/raydium/createRemoveLiquidityInstruction' + +const RemoveLiquidityRaydium = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + // const { getGovernancesByAccountType } = useGovernanceAssets() + const [governedAccounts, setGovernedAccounts] = useState< + GovernedMultiTypeAccount[] + >([]) + const { + governancesArray, + governedTokenAccounts, + getMintWithGovernances, + } = useGovernanceAssets() + // const governedProgramAccounts = getGovernancesByAccountType( + // GovernanceAccountType.ProgramGovernance + // ).map((x) => { + // return { + // governance: x, + // } + // }) + + useEffect(() => { + async function prepGovernances() { + const mintWithGovernances = await getMintWithGovernances() + const matchedGovernances = governancesArray.map((gov) => { + const governedTokenAccount = governedTokenAccounts.find( + (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() + ) + const mintGovernance = mintWithGovernances.find( + (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() + ) + if (governedTokenAccount) { + return governedTokenAccount as GovernedMultiTypeAccount + } + if (mintGovernance) { + return mintGovernance as GovernedMultiTypeAccount + } + return { + governance: gov, + } + }) + setGovernedAccounts(matchedGovernances) + } + prepGovernances() + }, []) + + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [form, setForm] = useState({ + governedAccount: undefined, + amountIn: 0, + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + + // const [, setMaxLPAmount] = useState(0) + // const [, setLpDecimals] = useState(9) + useEffect(() => { + // const fetchLpData = async () => { + // if (!form.governedAccount?.governance.pubkey) return + // const { maxBalance, decimals } = await getLPMintInfo( + // connection, + // UXP_USDC_POOL_KEYS.lpMint, + // form.governedAccount.governance.pubkey + // ) + // setMaxLPAmount(maxBalance) + // setLpDecimals(decimals) + // } + // fetchLpData() + }, [form.governedAccount?.governance.pubkey]) + + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + async function getInstruction(): Promise { + const isValid = await validateInstruction() + let serializedInstruction = '' + if ( + isValid && + programId && + form.governedAccount?.governance?.account && + wallet?.publicKey + ) { + const baseToken = getGovernanceToken( + connection.cluster, + form.baseTokenName + ) + + const createIx = createRemoveLiquidityInstruction( + new PublicKey(baseToken.address), + new BN(form.baseAmountIn * 10 ** baseToken.decimals) + ) + serializedInstruction = serializeInstructionToBase64(createIx) + } + const obj: UiInstruction = { + serializedInstruction, + isValid, + governance: form.governedAccount?.governance, + } + return obj + } + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [realmInfo?.programId]) + + useEffect(() => { + if (form.baseTokenName) { + debounce.debounceFcn(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + // We are assuming for now that we only have one Liquidity Pool (UXP/USDC) + handleSetForm({ + value: getGovernanceMintSymbols(connection.cluster).filter( + (s) => s !== form.baseTokenName + )[0], + propertyName: 'quoteTokenName', + }) + } + }, [form.baseTokenName]) + + useEffect(() => { + if (form.baseAmountIn) { + debounce.debounceFcn(async () => { + handleSetForm({ + value: await getAmountOut( + UXP_USDC_POOL_KEYS, + form.baseTokenName, + form.baseAmountIn, + form.quoteTokenName, + connection + ), + propertyName: 'quoteAmountIn', + }) + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + }) + } + }, [form.baseAmountIn]) + + useEffect(() => { + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) + + const schema = yup.object().shape({ + baseTokenName: yup.string().required('Base Token Name is required'), + quoteTokenName: yup.string().required('Quote Token Name is required'), + baseAmountIn: yup + .number() + .moreThan(0, 'Amount for base token should be more than 0') + .required('Amount for base token is required'), + quoteAmountIn: yup + .number() + .moreThan(0, 'Amount for quote token should be more than 0') + .required('Amount for quote token is required'), + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + fixedSide: yup + .string() + .equals(['base', 'quote']) + .required('Fixed Side is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + > + + + + + + handleSetForm({ + value: evt.target.value, + propertyName: 'baseAmountIn', + }) + } + error={formErrors['baseAmountIn']} + /> + + + handleSetForm({ + value: evt.target.value, + propertyName: 'quoteAmountIn', + }) + } + disabled={true} + error={formErrors['quoteAmountIn']} + /> + + + ) +} + +export default RemoveLiquidityRaydium diff --git a/tools/sdk/raydium/createRemoveLiquidityInstruction.ts b/tools/sdk/raydium/createRemoveLiquidityInstruction.ts new file mode 100644 index 0000000000..d08d5e7e0d --- /dev/null +++ b/tools/sdk/raydium/createRemoveLiquidityInstruction.ts @@ -0,0 +1,32 @@ +import { BN } from '@project-serum/anchor' +import { Liquidity } from '@raydium-io/raydium-sdk' +import { TransactionInstruction, PublicKey } from '@solana/web3.js' +import { findATAAddrSync } from '@uxdprotocol/uxd-client' +import { UXP_USDC_POOL_KEYS } from './poolKeys' + +// FIXME: missing parameter to select to correct pool +// only working now because we have just one pool (UXP/USDC) +export const createRemoveLiquidityInstruction = ( + owner: PublicKey, + amountIn: BN // amount of LP token to redeem? +): TransactionInstruction => { + const [lpTokenAccount] = findATAAddrSync(owner, UXP_USDC_POOL_KEYS.lpMint) + const [baseTokenAccount] = findATAAddrSync(owner, UXP_USDC_POOL_KEYS.baseMint) + const [quoteTokenAccount] = findATAAddrSync( + owner, + UXP_USDC_POOL_KEYS.quoteMint + ) + + const itx = Liquidity.makeRemoveLiquidityInstruction({ + poolKeys: UXP_USDC_POOL_KEYS, + userKeys: { + baseTokenAccount, + quoteTokenAccount, + lpTokenAccount, + owner, + }, + amountIn, + }) + + return itx +} diff --git a/tools/sdk/raydium/helpers.ts b/tools/sdk/raydium/helpers.ts index b90a337389..b75460de5b 100644 --- a/tools/sdk/raydium/helpers.ts +++ b/tools/sdk/raydium/helpers.ts @@ -3,12 +3,14 @@ import { Liquidity, LiquidityPoolKeys, Percent, - Token, TokenAmount, + Token, } from '@raydium-io/raydium-sdk' -import { PublicKey } from '@solana/web3.js' +import { PublicKey, Connection } from '@solana/web3.js' import { ConnectionContext } from '@utils/connection' +import { findATAAddrSync } from '@uxdprotocol/uxd-client' import { getGovernanceToken } from '../uxdProtocol/uxdClient' +import { Token as SPLToken } from '@solana/spl-token' export const getAmountOut = async ( poolKeys: LiquidityPoolKeys, @@ -45,3 +47,20 @@ export const getAmountOut = async ( return Number(currentPrice) * amountIn } + +export const getLPMintInfo = async ( + connection: ConnectionContext, + lpMint: PublicKey, + user: PublicKey +) => { + const [lpTokenAccount] = findATAAddrSync(user, lpMint) + const lpInfo = await connection.current.getTokenSupply(lpMint) + const lpUserBalance = await connection.current.getTokenAccountBalance( + lpTokenAccount + ) + return { + lpTokenAccount, + maxBalance: lpUserBalance.value.uiAmount ?? 0, + decimals: lpInfo.value.decimals, + } +} diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 3e14712be3..b8958d7d8c 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -81,6 +81,11 @@ export interface AddLiquidityRaydiumForm { fixedSide: AmountSide } +export interface RemoveLiquidityRaydiumForm { + governedAccount: GovernedMultiTypeAccount | undefined + amountIn: number +} + export interface MangoMakeChangeMaxAccountsForm { governedAccount: GovernedProgramAccount | undefined programId: string | undefined From 73e79c66eec86365264e78514c5db50696b043b5 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sat, 29 Jan 2022 23:02:24 +0900 Subject: [PATCH 088/204] fix routing (back to realms-explorer) --- tools/routing.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/routing.ts b/tools/routing.ts index ee98dd4aa2..fc28a11e09 100644 --- a/tools/routing.ts +++ b/tools/routing.ts @@ -4,8 +4,6 @@ export function getRealmExplorerHost(realmInfo: RealmInfo | undefined) { switch (realmInfo?.symbol) { case 'MNGO': return 'dao.mango.markets' - case 'UXD': - return 'governance.uxd.fi' default: return 'realms-explorer.com' } From 9bd70faf2733c47b67fb2abfd090587d067f0a56 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 30 Jan 2022 16:13:45 +0900 Subject: [PATCH 089/204] regroup uxd itxs form into UXD folder --- .../{ => UXD}/DepositInsuranceToMangoDepository.tsx | 4 ++-- .../instructions/{ => UXD}/InitializeController.tsx | 4 ++-- .../{ => UXD}/RegisterMangoDepository.tsx | 4 ++-- .../SetMangoDepositoriesRedeemableSoftCap.tsx | 4 ++-- .../{ => UXD}/SetRedeemGlobalSupplyCap.tsx | 4 ++-- .../WithdrawInsuranceFromMangoDepository.tsx | 4 ++-- pages/dao/[symbol]/proposal/new.tsx | 12 ++++++------ 7 files changed, 18 insertions(+), 18 deletions(-) rename pages/dao/[symbol]/proposal/components/instructions/{ => UXD}/DepositInsuranceToMangoDepository.tsx (98%) rename pages/dao/[symbol]/proposal/components/instructions/{ => UXD}/InitializeController.tsx (97%) rename pages/dao/[symbol]/proposal/components/instructions/{ => UXD}/RegisterMangoDepository.tsx (98%) rename pages/dao/[symbol]/proposal/components/instructions/{ => UXD}/SetMangoDepositoriesRedeemableSoftCap.tsx (97%) rename pages/dao/[symbol]/proposal/components/instructions/{ => UXD}/SetRedeemGlobalSupplyCap.tsx (97%) rename pages/dao/[symbol]/proposal/components/instructions/{ => UXD}/WithdrawInsuranceFromMangoDepository.tsx (98%) diff --git a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/DepositInsuranceToMangoDepository.tsx similarity index 98% rename from pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx rename to pages/dao/[symbol]/proposal/components/instructions/UXD/DepositInsuranceToMangoDepository.tsx index 3851563b9c..370a3fb310 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/DepositInsuranceToMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/DepositInsuranceToMangoDepository.tsx @@ -8,12 +8,12 @@ import { UiInstruction, DepositInsuranceToMangoDepositoryForm, } from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../new' +import { NewProposalContext } from '../../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' -import GovernedAccountSelect from '../GovernedAccountSelect' +import GovernedAccountSelect from '../../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createDepositInsuranceToMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction' import Select from '@components/inputs/Select' diff --git a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/InitializeController.tsx similarity index 97% rename from pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx rename to pages/dao/[symbol]/proposal/components/instructions/UXD/InitializeController.tsx index 53b5373122..eabf086aa8 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/InitializeController.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/InitializeController.tsx @@ -8,11 +8,11 @@ import { UiInstruction, InitializeControllerForm, } from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../new' +import { NewProposalContext } from '../../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' import Input from '@components/inputs/Input' -import GovernedAccountSelect from '../GovernedAccountSelect' +import GovernedAccountSelect from '../../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createInitializeControllerInstruction from '@tools/sdk/uxdProtocol/createInitializeControllerInstruction' import { diff --git a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/RegisterMangoDepository.tsx similarity index 98% rename from pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx rename to pages/dao/[symbol]/proposal/components/instructions/UXD/RegisterMangoDepository.tsx index 7263fc851f..72cef44aa3 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/RegisterMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/RegisterMangoDepository.tsx @@ -8,11 +8,11 @@ import { UiInstruction, RegisterMangoDepositoryForm, } from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../new' +import { NewProposalContext } from '../../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' import { debounce } from '@utils/debounce' -import GovernedAccountSelect from '../GovernedAccountSelect' +import GovernedAccountSelect from '../../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createRegisterMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction' import Select from '@components/inputs/Select' diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap.tsx similarity index 97% rename from pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx rename to pages/dao/[symbol]/proposal/components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap.tsx index c89bfbafcc..fa97a38a04 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -8,12 +8,12 @@ import { UiInstruction, SetMangoDepositoriesRedeemableSoftCapForm, } from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../new' +import { NewProposalContext } from '../../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' -import GovernedAccountSelect from '../GovernedAccountSelect' +import GovernedAccountSelect from '../../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createSetMangoDepositoriesRedeemableSoftCapInstruction from '@tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction' import { diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetRedeemGlobalSupplyCap.tsx similarity index 97% rename from pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx rename to pages/dao/[symbol]/proposal/components/instructions/UXD/SetRedeemGlobalSupplyCap.tsx index 87cd49f6e0..a6b0a4cd42 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetRedeemGlobalSupplyCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetRedeemGlobalSupplyCap.tsx @@ -8,12 +8,12 @@ import { UiInstruction, SetRedeemableGlobalSupplyCapForm, } from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../new' +import { NewProposalContext } from '../../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' -import GovernedAccountSelect from '../GovernedAccountSelect' +import GovernedAccountSelect from '../../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createSetRedeemableGlobalSupplyCapInstruction from '@tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction' import { diff --git a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/WithdrawInsuranceFromMangoDepository.tsx similarity index 98% rename from pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx rename to pages/dao/[symbol]/proposal/components/instructions/UXD/WithdrawInsuranceFromMangoDepository.tsx index b08a8dad69..915e2b396a 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/WithdrawInsuranceFromMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/WithdrawInsuranceFromMangoDepository.tsx @@ -8,7 +8,7 @@ import { UiInstruction, WithdrawInsuranceFromMangoDepositoryForm, } from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../new' +import { NewProposalContext } from '../../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' import { @@ -19,7 +19,7 @@ import { } from '@solana/spl-governance' import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' -import GovernedAccountSelect from '../GovernedAccountSelect' +import GovernedAccountSelect from '../../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' import createWithdrawInsuranceFromMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction' import Select from '@components/inputs/Select' diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index d5e330e6ac..0e363ecff9 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -35,12 +35,12 @@ import Empty from './components/instructions/Empty' import Mint from './components/instructions/Mint' import CustomBase64 from './components/instructions/CustomBase64' import { getTimestampFromDays } from '@tools/sdk/units' -import InitializeController from './components/instructions/InitializeController' -import SetRedeemGlobalSupplyCap from './components/instructions/SetRedeemGlobalSupplyCap' -import RegisterMangoDepository from './components/instructions/RegisterMangoDepository' -import SetMangoDepositoriesRedeemableSoftCap from './components/instructions/SetMangoDepositoriesRedeemableSoftCap' -import DepositInsuranceToMangoDepository from './components/instructions/DepositInsuranceToMangoDepository' -import WithdrawInsuranceFromMangoDepository from './components/instructions/WithdrawInsuranceFromMangoDepository' +import InitializeController from './components/instructions/UXD/InitializeController' +import SetRedeemGlobalSupplyCap from './components/instructions/UXD/SetRedeemGlobalSupplyCap' +import RegisterMangoDepository from './components/instructions/UXD/RegisterMangoDepository' +import SetMangoDepositoriesRedeemableSoftCap from './components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap' +import DepositInsuranceToMangoDepository from './components/instructions/UXD/DepositInsuranceToMangoDepository' +import WithdrawInsuranceFromMangoDepository from './components/instructions/UXD/WithdrawInsuranceFromMangoDepository' import MakeChangeMaxAccounts from './components/instructions/Mango/MakeChangeMaxAccounts' import VoteBySwitch from './components/VoteBySwitch' import TokenBalanceCardWrapper from '@components/TokenBalance/TokenBalanceCardWrapper' From b529ab2a00fb0f2c1978cdc80fbe981694aef1e2 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 30 Jan 2022 16:36:00 +0900 Subject: [PATCH 090/204] update Instructions list and names --- hooks/useGovernanceAssets.ts | 19 ++++++++++++------- pages/dao/[symbol]/proposal/new.tsx | 3 +++ utils/uiTypes/proposalCreationTypes.ts | 1 + 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 8225d19162..e0e9a95b58 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -217,37 +217,42 @@ export default function useGovernanceAssets() { }, { id: Instructions.InitializeController, - name: 'Initialize Controller', + name: 'UXD: Initialize Controller', isVisible: canUseUxdInstructions, }, { id: Instructions.SetRedeemableGlobalSupplyCap, - name: 'Set Redeemable Global Supply Cap', + name: 'UXD: Set Redeemable Global Supply Cap', isVisible: canUseUxdInstructions, }, { id: Instructions.SetMangoDepositoriesRedeemableSoftCap, - name: 'Set Mango Depositories Redeemable Supply Soft Cap', + name: 'UXD: Set Mango Depositories Redeemable Supply Soft Cap', isVisible: canUseUxdInstructions, }, { id: Instructions.RegisterMangoDepository, - name: 'Register Mango Depository', + name: 'UXD: Register Mango Depository', isVisible: canUseUxdInstructions, }, { id: Instructions.DepositInsuranceToMangoDepository, - name: 'Deposit Insurance To Mango Depository', + name: 'UXD: Deposit Insurance To Mango Depository', isVisible: canUseUxdInstructions, }, { id: Instructions.WithdrawInsuranceFromMangoDepository, - name: 'Withdraw Insurance From Mango Depository', + name: 'UXD: Withdraw Insurance From Mango Depository', isVisible: canUseUxdInstructions, }, { id: Instructions.AddLiquidityRaydium, - name: 'Add To Raydium Liquidity Pool', + name: 'Raydium: Add To Liquidity Pool', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.RemoveLiquidityRaydium, + name: 'Raydium: Remove From Liquidity Pool', isVisible: canUseAnyInstruction, }, { diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index 0e363ecff9..f7937909cf 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -56,6 +56,7 @@ import RefreshObligation from './components/instructions/solend/RefreshObligatio import RefreshReserve from './components/instructions/solend/RefreshReserve' import Grant from 'VoteStakeRegistry/components/instructions/Grant' import Clawback from 'VoteStakeRegistry/components/instructions/Clawback' +import RemoveLiquidityRaydium from './components/instructions/raydium/RemoveLiquidity' const schema = yup.object().shape({ title: yup.string().required('Title is required'), @@ -355,6 +356,8 @@ const New = () => { ) case Instructions.AddLiquidityRaydium: return + case Instructions.RemoveLiquidityRaydium: + return case Instructions.InitializeController: return case Instructions.SetRedeemableGlobalSupplyCap: diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index b8958d7d8c..73b713bda4 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -146,6 +146,7 @@ export enum Instructions { Base64, None, AddLiquidityRaydium, + RemoveLiquidityRaydium, InitializeController, SetRedeemableGlobalSupplyCap, SetMangoDepositoriesRedeemableSoftCap, From 03cabb28d2f47d8e0710dd051a9e85868d5d3ad2 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 30 Jan 2022 21:58:54 +0900 Subject: [PATCH 091/204] Remove Liquidity simulation passing (but itx too > 1232bytes) --- .../instructions/raydium/AddLiquidity.tsx | 7 -- .../instructions/raydium/RemoveLiquidity.tsx | 114 ++++-------------- .../createRemoveLiquidityInstruction.ts | 4 +- tools/sdk/raydium/helpers.ts | 3 +- 4 files changed, 28 insertions(+), 100 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx index c220e78b27..88854475b4 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx @@ -49,13 +49,6 @@ const AddLiquidityRaydium = ({ governedTokenAccounts, getMintWithGovernances, } = useGovernanceAssets() - // const governedProgramAccounts = getGovernancesByAccountType( - // GovernanceAccountType.ProgramGovernance - // ).map((x) => { - // return { - // governance: x, - // } - // }) useEffect(() => { async function prepGovernances() { diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx index 2634d0a89d..7d0a098668 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx @@ -12,20 +12,17 @@ import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' import { GovernedMultiTypeAccount } from '@utils/tokens' import Select from '@components/inputs/Select' -import { - getGovernanceMintSymbols, - getGovernanceToken, -} from '@tools/sdk/uxdProtocol/uxdClient' +import { getGovernanceMintSymbols } from '@tools/sdk/uxdProtocol/uxdClient' import { ProgramAccount, serializeInstructionToBase64, Governance, } from '@solana/spl-governance' import GovernedAccountSelect from '../../GovernedAccountSelect' -import { getAmountOut } from '@tools/sdk/raydium/helpers' +import { getLPMintInfo } from '@tools/sdk/raydium/helpers' import { UXP_USDC_POOL_KEYS } from '@tools/sdk/raydium/poolKeys' -import { BN } from '@project-serum/anchor' import { createRemoveLiquidityInstruction } from '@tools/sdk/raydium/createRemoveLiquidityInstruction' +import BigNumber from 'bignumber.js' const RemoveLiquidityRaydium = ({ index, @@ -46,13 +43,6 @@ const RemoveLiquidityRaydium = ({ governedTokenAccounts, getMintWithGovernances, } = useGovernanceAssets() - // const governedProgramAccounts = getGovernancesByAccountType( - // GovernanceAccountType.ProgramGovernance - // ).map((x) => { - // return { - // governance: x, - // } - // }) useEffect(() => { async function prepGovernances() { @@ -88,20 +78,20 @@ const RemoveLiquidityRaydium = ({ const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) - // const [, setMaxLPAmount] = useState(0) - // const [, setLpDecimals] = useState(9) + const [maxLPAmount, setMaxLPAmount] = useState(0) + const [LPDecimals, setLPDecimals] = useState(9) useEffect(() => { - // const fetchLpData = async () => { - // if (!form.governedAccount?.governance.pubkey) return - // const { maxBalance, decimals } = await getLPMintInfo( - // connection, - // UXP_USDC_POOL_KEYS.lpMint, - // form.governedAccount.governance.pubkey - // ) - // setMaxLPAmount(maxBalance) - // setLpDecimals(decimals) - // } - // fetchLpData() + const fetchLpData = async () => { + if (!form.governedAccount?.governance.pubkey) return + const { maxBalance, decimals } = await getLPMintInfo( + connection, + UXP_USDC_POOL_KEYS.lpMint, + form.governedAccount.governance.pubkey + ) + setMaxLPAmount(maxBalance) + setLPDecimals(decimals) + } + fetchLpData() }, [form.governedAccount?.governance.pubkey]) const handleSetForm = ({ propertyName, value }) => { @@ -122,14 +112,9 @@ const RemoveLiquidityRaydium = ({ form.governedAccount?.governance?.account && wallet?.publicKey ) { - const baseToken = getGovernanceToken( - connection.cluster, - form.baseTokenName - ) - const createIx = createRemoveLiquidityInstruction( - new PublicKey(baseToken.address), - new BN(form.baseAmountIn * 10 ** baseToken.decimals) + new PublicKey(form.governedAccount.governance.pubkey), + new BigNumber(form.amountIn).shiftedBy(LPDecimals).toString() ) serializedInstruction = serializeInstructionToBase64(createIx) } @@ -166,16 +151,6 @@ const RemoveLiquidityRaydium = ({ useEffect(() => { if (form.baseAmountIn) { debounce.debounceFcn(async () => { - handleSetForm({ - value: await getAmountOut( - UXP_USDC_POOL_KEYS, - form.baseTokenName, - form.baseAmountIn, - form.quoteTokenName, - connection - ), - propertyName: 'quoteAmountIn', - }) const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) }) @@ -192,22 +167,14 @@ const RemoveLiquidityRaydium = ({ const schema = yup.object().shape({ baseTokenName: yup.string().required('Base Token Name is required'), quoteTokenName: yup.string().required('Quote Token Name is required'), - baseAmountIn: yup - .number() - .moreThan(0, 'Amount for base token should be more than 0') - .required('Amount for base token is required'), - quoteAmountIn: yup + amountIn: yup .number() - .moreThan(0, 'Amount for quote token should be more than 0') - .required('Amount for quote token is required'), + .moreThan(0, 'Amount for LP token should be more than 0') + .required('Amount for LP token is required'), governedAccount: yup .object() .nullable() .required('Program governed account is required'), - fixedSide: yup - .string() - .equals(['base', 'quote']) - .required('Fixed Side is required'), }) return ( @@ -257,50 +224,19 @@ const RemoveLiquidityRaydium = ({ - handleSetForm({ - value: evt.target.value, - propertyName: 'baseAmountIn', - }) - } - error={formErrors['baseAmountIn']} - /> - - handleSetForm({ value: evt.target.value, - propertyName: 'quoteAmountIn', + propertyName: 'amountIn', }) } - disabled={true} - error={formErrors['quoteAmountIn']} + error={formErrors['amountIn']} /> - ) } diff --git a/tools/sdk/raydium/createRemoveLiquidityInstruction.ts b/tools/sdk/raydium/createRemoveLiquidityInstruction.ts index d08d5e7e0d..0b2d451b6d 100644 --- a/tools/sdk/raydium/createRemoveLiquidityInstruction.ts +++ b/tools/sdk/raydium/createRemoveLiquidityInstruction.ts @@ -8,7 +8,7 @@ import { UXP_USDC_POOL_KEYS } from './poolKeys' // only working now because we have just one pool (UXP/USDC) export const createRemoveLiquidityInstruction = ( owner: PublicKey, - amountIn: BN // amount of LP token to redeem? + amountIn: string // amount of LP token to redeem? ): TransactionInstruction => { const [lpTokenAccount] = findATAAddrSync(owner, UXP_USDC_POOL_KEYS.lpMint) const [baseTokenAccount] = findATAAddrSync(owner, UXP_USDC_POOL_KEYS.baseMint) @@ -25,7 +25,7 @@ export const createRemoveLiquidityInstruction = ( lpTokenAccount, owner, }, - amountIn, + amountIn: new BN(amountIn), }) return itx diff --git a/tools/sdk/raydium/helpers.ts b/tools/sdk/raydium/helpers.ts index b75460de5b..a2e47e88c6 100644 --- a/tools/sdk/raydium/helpers.ts +++ b/tools/sdk/raydium/helpers.ts @@ -6,11 +6,10 @@ import { TokenAmount, Token, } from '@raydium-io/raydium-sdk' -import { PublicKey, Connection } from '@solana/web3.js' +import { PublicKey } from '@solana/web3.js' import { ConnectionContext } from '@utils/connection' import { findATAAddrSync } from '@uxdprotocol/uxd-client' import { getGovernanceToken } from '../uxdProtocol/uxdClient' -import { Token as SPLToken } from '@solana/spl-token' export const getAmountOut = async ( poolKeys: LiquidityPoolKeys, From cf19a7cf190ccd8e89712a5f5d40290a53e22637 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Tue, 1 Feb 2022 17:31:56 +0900 Subject: [PATCH 092/204] refactor remove from liquidity pool --- .../instructions/raydium/AddLiquidity.tsx | 3 +- .../instructions/raydium/RemoveLiquidity.tsx | 63 ++++++-------- .../raydium/createAddLiquidityInstruction.ts | 86 +++---------------- .../createRemoveLiquidityInstruction.ts | 17 ++-- tools/sdk/raydium/helpers.ts | 25 ++++++ tools/sdk/raydium/poolKeys.ts | 45 +++++++++- 6 files changed, 114 insertions(+), 125 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx index 88854475b4..aab9ca641c 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx @@ -123,8 +123,7 @@ const AddLiquidityRaydium = ({ ).toString() ), form.fixedSide, - form.governedAccount.governance.pubkey, - new PublicKey(wallet.publicKey.toBase58()) + form.governedAccount.governance.pubkey ) serializedInstruction = serializeInstructionToBase64(createIx) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx index 7d0a098668..980a039962 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx @@ -12,15 +12,17 @@ import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' import { GovernedMultiTypeAccount } from '@utils/tokens' import Select from '@components/inputs/Select' -import { getGovernanceMintSymbols } from '@tools/sdk/uxdProtocol/uxdClient' import { ProgramAccount, serializeInstructionToBase64, Governance, } from '@solana/spl-governance' import GovernedAccountSelect from '../../GovernedAccountSelect' -import { getLPMintInfo } from '@tools/sdk/raydium/helpers' -import { UXP_USDC_POOL_KEYS } from '@tools/sdk/raydium/poolKeys' +import { + getLiquidityPoolKeysByLabel, + getLPMintInfo, +} from '@tools/sdk/raydium/helpers' +import { liquidityPoolList } from '@tools/sdk/raydium/poolKeys' import { createRemoveLiquidityInstruction } from '@tools/sdk/raydium/createRemoveLiquidityInstruction' import BigNumber from 'bignumber.js' @@ -82,17 +84,20 @@ const RemoveLiquidityRaydium = ({ const [LPDecimals, setLPDecimals] = useState(9) useEffect(() => { const fetchLpData = async () => { - if (!form.governedAccount?.governance.pubkey) return + if (!form.governedAccount?.governance.pubkey || !form.liquidityPool) + return + const poolKeys = getLiquidityPoolKeysByLabel(form.liquidityPool) + const { maxBalance, decimals } = await getLPMintInfo( connection, - UXP_USDC_POOL_KEYS.lpMint, + poolKeys.lpMint, form.governedAccount.governance.pubkey ) setMaxLPAmount(maxBalance) setLPDecimals(decimals) } fetchLpData() - }, [form.governedAccount?.governance.pubkey]) + }, [form.governedAccount?.governance.pubkey, form.liquidityPool]) const handleSetForm = ({ propertyName, value }) => { setFormErrors({}) @@ -114,6 +119,7 @@ const RemoveLiquidityRaydium = ({ ) { const createIx = createRemoveLiquidityInstruction( new PublicKey(form.governedAccount.governance.pubkey), + form.liquidityPool, new BigNumber(form.amountIn).shiftedBy(LPDecimals).toString() ) serializedInstruction = serializeInstructionToBase64(createIx) @@ -133,20 +139,17 @@ const RemoveLiquidityRaydium = ({ }, [realmInfo?.programId]) useEffect(() => { - if (form.baseTokenName) { + if (form.liquidityPool) { debounce.debounceFcn(async () => { const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) }) - // We are assuming for now that we only have one Liquidity Pool (UXP/USDC) handleSetForm({ - value: getGovernanceMintSymbols(connection.cluster).filter( - (s) => s !== form.baseTokenName - )[0], - propertyName: 'quoteTokenName', + value: form.liquidityPool, + propertyName: 'liquidityPool', }) } - }, [form.baseTokenName]) + }, [form.liquidityPool]) useEffect(() => { if (form.baseAmountIn) { @@ -165,8 +168,7 @@ const RemoveLiquidityRaydium = ({ }, [form]) const schema = yup.object().shape({ - baseTokenName: yup.string().required('Base Token Name is required'), - quoteTokenName: yup.string().required('Quote Token Name is required'), + liquidityPool: yup.string().required('Liquidity Pool is required'), amountIn: yup .number() .moreThan(0, 'Amount for LP token should be more than 0') @@ -191,34 +193,17 @@ const RemoveLiquidityRaydium = ({ governance={governance} > - - diff --git a/tools/sdk/raydium/createAddLiquidityInstruction.ts b/tools/sdk/raydium/createAddLiquidityInstruction.ts index b8f8834893..617481b81d 100644 --- a/tools/sdk/raydium/createAddLiquidityInstruction.ts +++ b/tools/sdk/raydium/createAddLiquidityInstruction.ts @@ -1,15 +1,8 @@ import { BN } from '@project-serum/anchor' -import { - AmountSide, - ASSOCIATED_TOKEN_PROGRAM_ID, - Liquidity, - TOKEN_PROGRAM_ID, -} from '@raydium-io/raydium-sdk' -import { serializeInstructionToBase64 } from '@solana/spl-governance' -import { Token } from '@solana/spl-token' +import { AmountSide, Liquidity } from '@raydium-io/raydium-sdk' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { findATAAddrSync } from '@uxdprotocol/uxd-client' -import { UXP_USDC_POOL_KEYS } from './poolKeys' +import { getLiquidityPoolKeysByAssets } from './helpers' export const createAddLiquidityInstruction = ( tokenMintA: PublicKey, //TokenA we want to deposit (USDC or UXP) @@ -17,81 +10,26 @@ export const createAddLiquidityInstruction = ( amountA: BN, amountB: BN, fixedSide: AmountSide, - owner: PublicKey, - payer: PublicKey + owner: PublicKey ): TransactionInstruction => { - const [lpTokenAccount] = findATAAddrSync(owner, UXP_USDC_POOL_KEYS.lpMint) - console.log( - 'lp token account to create if not exist: ', - lpTokenAccount.toBase58() - ) + const poolKeys = getLiquidityPoolKeysByAssets(tokenMintA, tokenMintB) - const createlpTokenAccountItx = Token.createAssociatedTokenAccountInstruction( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - UXP_USDC_POOL_KEYS.lpMint, - lpTokenAccount, - owner, // owner - payer // payer - ) - - console.log( - `Initialize Authority LP token ATA (${lpTokenAccount.toBase58()}) itx:`, - serializeInstructionToBase64(createlpTokenAccountItx) - ) - - const [baseTokenAccount] = findATAAddrSync(owner, UXP_USDC_POOL_KEYS.baseMint) - console.log( - 'usdc token account to create if not exist: ', - baseTokenAccount.toBase58() - ) - - const createBaseTokenAccountItx = Token.createAssociatedTokenAccountInstruction( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - UXP_USDC_POOL_KEYS.quoteMint, - baseTokenAccount, - owner, // owner - payer // payer - ) - - console.log( - `Initialize Authority BASE token ATA (${baseTokenAccount.toBase58()}) itx:`, - serializeInstructionToBase64(createBaseTokenAccountItx) - ) - - const [quoteTokenAccount] = findATAAddrSync( - owner, - UXP_USDC_POOL_KEYS.quoteMint - ) - console.log( - 'usdc token account to create if not exist: ', - quoteTokenAccount.toBase58() - ) - - const createQuoteTokenAccountItx = Token.createAssociatedTokenAccountInstruction( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - UXP_USDC_POOL_KEYS.quoteMint, - quoteTokenAccount, - owner, // owner - payer // payer - ) - - console.log( - `Initialize Authority QUOTE token ATA (${quoteTokenAccount.toBase58()}) itx:`, - serializeInstructionToBase64(createQuoteTokenAccountItx) - ) + if (!poolKeys) { + throw new Error('pool not found') + } + const [lpTokenAccount] = findATAAddrSync(owner, poolKeys.lpMint) + const [baseTokenAccount] = findATAAddrSync(owner, poolKeys.baseMint) + const [quoteTokenAccount] = findATAAddrSync(owner, poolKeys.quoteMint) let baseAmountIn = amountA let quoteAmountIn = amountB - if (tokenMintA.equals(UXP_USDC_POOL_KEYS.quoteMint)) { + if (tokenMintA.equals(poolKeys.quoteMint)) { baseAmountIn = amountB quoteAmountIn = amountA } const itx = Liquidity.makeAddLiquidityInstruction({ - poolKeys: UXP_USDC_POOL_KEYS, + poolKeys, userKeys: { baseTokenAccount, quoteTokenAccount, diff --git a/tools/sdk/raydium/createRemoveLiquidityInstruction.ts b/tools/sdk/raydium/createRemoveLiquidityInstruction.ts index 0b2d451b6d..b446afe9d3 100644 --- a/tools/sdk/raydium/createRemoveLiquidityInstruction.ts +++ b/tools/sdk/raydium/createRemoveLiquidityInstruction.ts @@ -2,23 +2,22 @@ import { BN } from '@project-serum/anchor' import { Liquidity } from '@raydium-io/raydium-sdk' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { findATAAddrSync } from '@uxdprotocol/uxd-client' -import { UXP_USDC_POOL_KEYS } from './poolKeys' +import { getLiquidityPoolKeysByLabel } from './helpers' // FIXME: missing parameter to select to correct pool // only working now because we have just one pool (UXP/USDC) export const createRemoveLiquidityInstruction = ( owner: PublicKey, - amountIn: string // amount of LP token to redeem? + liquidityPool: string, + amountIn: string ): TransactionInstruction => { - const [lpTokenAccount] = findATAAddrSync(owner, UXP_USDC_POOL_KEYS.lpMint) - const [baseTokenAccount] = findATAAddrSync(owner, UXP_USDC_POOL_KEYS.baseMint) - const [quoteTokenAccount] = findATAAddrSync( - owner, - UXP_USDC_POOL_KEYS.quoteMint - ) + const poolKeys = getLiquidityPoolKeysByLabel(liquidityPool) + const [lpTokenAccount] = findATAAddrSync(owner, poolKeys.lpMint) + const [baseTokenAccount] = findATAAddrSync(owner, poolKeys.baseMint) + const [quoteTokenAccount] = findATAAddrSync(owner, poolKeys.quoteMint) const itx = Liquidity.makeRemoveLiquidityInstruction({ - poolKeys: UXP_USDC_POOL_KEYS, + poolKeys, userKeys: { baseTokenAccount, quoteTokenAccount, diff --git a/tools/sdk/raydium/helpers.ts b/tools/sdk/raydium/helpers.ts index a2e47e88c6..db27112ea7 100644 --- a/tools/sdk/raydium/helpers.ts +++ b/tools/sdk/raydium/helpers.ts @@ -10,6 +10,8 @@ import { PublicKey } from '@solana/web3.js' import { ConnectionContext } from '@utils/connection' import { findATAAddrSync } from '@uxdprotocol/uxd-client' import { getGovernanceToken } from '../uxdProtocol/uxdClient' +import { liquidityPoolKeys, liquidityPoolList } from './poolKeys' +export { liquidityPoolKeys } from './poolKeys' export const getAmountOut = async ( poolKeys: LiquidityPoolKeys, @@ -63,3 +65,26 @@ export const getLPMintInfo = async ( decimals: lpInfo.value.decimals, } } + +export const getLiquidityPoolKeysByAssets = ( + base: PublicKey, + quote: PublicKey +): LiquidityPoolKeys | undefined => { + return liquidityPoolKeys.find( + (pk) => + (pk.baseMint.equals(base) && pk.quoteMint.equals(quote)) || + (pk.baseMint.equals(quote) && pk.quoteMint.equals(base)) + ) +} + +export const getLiquidityPoolKeysByLabel = ( + label: string +): LiquidityPoolKeys => { + const lp = liquidityPoolList.find((lp) => lp.label === label)?.id + if (!lp) throw new Error('pool not found for label ' + label) + + const poolKeys = liquidityPoolKeys.find((lpk) => lpk.id.equals(lp)) + if (!poolKeys) throw new Error('pool not found for id ' + lp.toBase58()) + + return poolKeys +} diff --git a/tools/sdk/raydium/poolKeys.ts b/tools/sdk/raydium/poolKeys.ts index 37be115a4c..4d3bc00b0e 100644 --- a/tools/sdk/raydium/poolKeys.ts +++ b/tools/sdk/raydium/poolKeys.ts @@ -1,6 +1,6 @@ import { LiquidityPoolKeys, jsonInfo2PoolKeys } from '@raydium-io/raydium-sdk' +import { PublicKey } from '@solana/web3.js' -// fetched from https://sdk.raydium.io/liquidity/mainnet.json export const UXP_USDC_POOL_KEYS: LiquidityPoolKeys = jsonInfo2PoolKeys({ id: '6tmFJbMk5yVHFcFy7X2K8RwHjKLr6KVFLYXpgpBNeAxB', baseMint: 'UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M', @@ -25,3 +25,46 @@ export const UXP_USDC_POOL_KEYS: LiquidityPoolKeys = jsonInfo2PoolKeys({ marketAsks: '68xmWKfE32qDoFL4iKBsjKpDyAfaiW4efdTDSAm33sKj', marketEventQueue: 'Cta4TEwKCKhSphkMNzXsURVr2V6mozm2SPaV8tCgDPwy', }) + +// fetch poolkeys from https://sdk.raydium.io/liquidity/mainnet.json +const poolKeys = [ + { + /** + * ============= UXP/USDC ============= + */ + id: '6tmFJbMk5yVHFcFy7X2K8RwHjKLr6KVFLYXpgpBNeAxB', + baseMint: 'UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M', + quoteMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + lpMint: 'AyuurXCCF2KdYTURbN3JsDKzZFZSiRqKc8UcZnrgBsGm', + version: 4, + programId: '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8', + authority: '5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1', + openOrders: '7AP5KPxkc9TYtYvqyXc4RK9GRVutGSne8Pj4ryKJoY4Z', + targetOrders: 'DfEhXNWDjsDNz1bqz6GinQU8RepjFneosamAM2XZ3heT', + baseVault: '3Dtb2kDA3pJkUrULXmQa8qn1RkmgnEM4eo2nf6Uuq3K3', + quoteVault: 'Gh2YaVC1sjzZQMixnHNXDin6awBAV6p2D5zY8STMu4p4', + withdrawQueue: '27BsfZSe59K2WXbhGGrvpySTRhd12moxxdLpYm6coFDT', + lpVault: '2dbkq546TV6C7Dmx5HWdHx7sTf6tpetvryqDRrcsE7kQ', + marketVersion: 3, + marketProgramId: '9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin', + marketId: '7KQpsp914VYnh62yV6AGfoG9hprfA14SgzEyqr6u9NY1', + marketAuthority: '5F4DUyyDR2uH7VTADLzi1CFmsVBVqPXk4TM4yHf9WDJi', + marketBaseVault: '9QGayBN3ycectkhLKiTPcfM9iFVtFpefSGWRr3XUoLwk', + marketQuoteVault: 'EiVf38NCvDFVJQqF5FgX1zeQ26Mzr88iELFugUSMJzu9', + marketBids: 'L6vnHnDLf8EPKXyaNAyhpkCdocvtkpNX8euVFZtqjCQ', + marketAsks: '68xmWKfE32qDoFL4iKBsjKpDyAfaiW4efdTDSAm33sKj', + marketEventQueue: 'Cta4TEwKCKhSphkMNzXsURVr2V6mozm2SPaV8tCgDPwy', + }, + // Add other Raydium liquidity pools here +] + +export const liquidityPoolKeys: LiquidityPoolKeys[] = poolKeys.map((pk) => + jsonInfo2PoolKeys(pk) +) + +export const liquidityPoolList = [ + { + label: 'UXP - USDC', + id: new PublicKey('6tmFJbMk5yVHFcFy7X2K8RwHjKLr6KVFLYXpgpBNeAxB'), + }, +] From 811b063bc5e8a7e6eaf67fb891968534a25e1b6d Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Tue, 1 Feb 2022 23:33:11 +0900 Subject: [PATCH 093/204] moar refactoar --- .../instructions/raydium/AddLiquidity.tsx | 23 +++++++++++- .../instructions/raydium/RemoveLiquidity.tsx | 35 ++++++++++--------- .../createRemoveLiquidityInstruction.ts | 8 ++--- tools/sdk/raydium/poolKeys.ts | 27 ++++++++++++++ utils/uiTypes/proposalCreationTypes.ts | 2 ++ 5 files changed, 72 insertions(+), 23 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx index aab9ca641c..d93404523d 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx @@ -27,7 +27,10 @@ import { import GovernedAccountSelect from '../../GovernedAccountSelect' import { createAddLiquidityInstruction } from '@tools/sdk/raydium/createAddLiquidityInstruction' import { getAmountOut } from '@tools/sdk/raydium/helpers' -import { UXP_USDC_POOL_KEYS } from '@tools/sdk/raydium/poolKeys' +import { + liquidityPoolList, + UXP_USDC_POOL_KEYS, +} from '@tools/sdk/raydium/poolKeys' import { BN } from '@project-serum/anchor' const AddLiquidityRaydium = ({ @@ -78,6 +81,7 @@ const AddLiquidityRaydium = ({ const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({ governedAccount: undefined, + liquidityPool: '', tokenAName: '', tokenBName: '', tokenAAmountIn: 0, @@ -134,6 +138,7 @@ const AddLiquidityRaydium = ({ } return obj } + useEffect(() => { handleSetForm({ propertyName: 'programId', @@ -217,6 +222,22 @@ const AddLiquidityRaydium = ({ shouldBeGoverned={shouldBeGoverned} governance={governance} > + + diff --git a/tools/sdk/raydium/createRemoveLiquidityInstruction.ts b/tools/sdk/raydium/createRemoveLiquidityInstruction.ts index b446afe9d3..93db5014dd 100644 --- a/tools/sdk/raydium/createRemoveLiquidityInstruction.ts +++ b/tools/sdk/raydium/createRemoveLiquidityInstruction.ts @@ -1,17 +1,13 @@ import { BN } from '@project-serum/anchor' -import { Liquidity } from '@raydium-io/raydium-sdk' +import { Liquidity, LiquidityPoolKeys } from '@raydium-io/raydium-sdk' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { findATAAddrSync } from '@uxdprotocol/uxd-client' -import { getLiquidityPoolKeysByLabel } from './helpers' -// FIXME: missing parameter to select to correct pool -// only working now because we have just one pool (UXP/USDC) export const createRemoveLiquidityInstruction = ( owner: PublicKey, - liquidityPool: string, + poolKeys: LiquidityPoolKeys, amountIn: string ): TransactionInstruction => { - const poolKeys = getLiquidityPoolKeysByLabel(liquidityPool) const [lpTokenAccount] = findATAAddrSync(owner, poolKeys.lpMint) const [baseTokenAccount] = findATAAddrSync(owner, poolKeys.baseMint) const [quoteTokenAccount] = findATAAddrSync(owner, poolKeys.quoteMint) diff --git a/tools/sdk/raydium/poolKeys.ts b/tools/sdk/raydium/poolKeys.ts index 4d3bc00b0e..7097d83379 100644 --- a/tools/sdk/raydium/poolKeys.ts +++ b/tools/sdk/raydium/poolKeys.ts @@ -68,3 +68,30 @@ export const liquidityPoolList = [ id: new PublicKey('6tmFJbMk5yVHFcFy7X2K8RwHjKLr6KVFLYXpgpBNeAxB'), }, ] + +export const liquidityPoolKeysList = { + 'UXP - USDC': { + id: '6tmFJbMk5yVHFcFy7X2K8RwHjKLr6KVFLYXpgpBNeAxB', + baseMint: 'UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M', + quoteMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + lpMint: 'AyuurXCCF2KdYTURbN3JsDKzZFZSiRqKc8UcZnrgBsGm', + version: 4, + programId: '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8', + authority: '5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1', + openOrders: '7AP5KPxkc9TYtYvqyXc4RK9GRVutGSne8Pj4ryKJoY4Z', + targetOrders: 'DfEhXNWDjsDNz1bqz6GinQU8RepjFneosamAM2XZ3heT', + baseVault: '3Dtb2kDA3pJkUrULXmQa8qn1RkmgnEM4eo2nf6Uuq3K3', + quoteVault: 'Gh2YaVC1sjzZQMixnHNXDin6awBAV6p2D5zY8STMu4p4', + withdrawQueue: '27BsfZSe59K2WXbhGGrvpySTRhd12moxxdLpYm6coFDT', + lpVault: '2dbkq546TV6C7Dmx5HWdHx7sTf6tpetvryqDRrcsE7kQ', + marketVersion: 3, + marketProgramId: '9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin', + marketId: '7KQpsp914VYnh62yV6AGfoG9hprfA14SgzEyqr6u9NY1', + marketAuthority: '5F4DUyyDR2uH7VTADLzi1CFmsVBVqPXk4TM4yHf9WDJi', + marketBaseVault: '9QGayBN3ycectkhLKiTPcfM9iFVtFpefSGWRr3XUoLwk', + marketQuoteVault: 'EiVf38NCvDFVJQqF5FgX1zeQ26Mzr88iELFugUSMJzu9', + marketBids: 'L6vnHnDLf8EPKXyaNAyhpkCdocvtkpNX8euVFZtqjCQ', + marketAsks: '68xmWKfE32qDoFL4iKBsjKpDyAfaiW4efdTDSAm33sKj', + marketEventQueue: 'Cta4TEwKCKhSphkMNzXsURVr2V6mozm2SPaV8tCgDPwy', + }, +} diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 73b713bda4..9f8357540f 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -74,6 +74,7 @@ export interface ProgramAuthorityForm { export interface AddLiquidityRaydiumForm { governedAccount: GovernedMultiTypeAccount | undefined + liquidityPool: string tokenAName: string tokenBName: string tokenAAmountIn: number @@ -83,6 +84,7 @@ export interface AddLiquidityRaydiumForm { export interface RemoveLiquidityRaydiumForm { governedAccount: GovernedMultiTypeAccount | undefined + liquidityPool: string amountIn: number } From c6e4e883b14fdea695543e7b9f3f3ff9c7de19b0 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 3 Feb 2022 00:51:40 +0900 Subject: [PATCH 094/204] add radium pool not dependant from uxp/uxdc anymore --- components/instructions/programs/splToken.tsx | 1 + .../instructions/raydium/AddLiquidity.tsx | 229 +++++++----------- .../raydium/createAddLiquidityInstruction.ts | 26 +- tools/sdk/raydium/helpers.ts | 24 +- utils/uiTypes/proposalCreationTypes.ts | 6 +- 5 files changed, 110 insertions(+), 176 deletions(-) diff --git a/components/instructions/programs/splToken.tsx b/components/instructions/programs/splToken.tsx index 2cf626a53f..b47b3f2f2f 100644 --- a/components/instructions/programs/splToken.tsx +++ b/components/instructions/programs/splToken.tsx @@ -15,6 +15,7 @@ export const MINT_METADATA = { '5oVNBeEEQvYi1cX3ir8Dx5n1P7pdxydbGF2X4TxVusJm': { name: 'SOCN' }, SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt: { name: 'SRM' }, MyHd6a7HWKTMeJMHBkrbMq4hZwZxwn9x7dxXcopQ4Wd: { name: 'OMH' }, + UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M: { name: 'UXP' }, } export function getMintMetadata( diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx index d93404523d..f06a1c7da9 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx @@ -15,10 +15,6 @@ import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' import { GovernedMultiTypeAccount } from '@utils/tokens' import Select from '@components/inputs/Select' -import { - getGovernanceMintSymbols, - getGovernanceToken, -} from '@tools/sdk/uxdProtocol/uxdClient' import { ProgramAccount, serializeInstructionToBase64, @@ -26,12 +22,13 @@ import { } from '@solana/spl-governance' import GovernedAccountSelect from '../../GovernedAccountSelect' import { createAddLiquidityInstruction } from '@tools/sdk/raydium/createAddLiquidityInstruction' -import { getAmountOut } from '@tools/sdk/raydium/helpers' import { - liquidityPoolList, - UXP_USDC_POOL_KEYS, -} from '@tools/sdk/raydium/poolKeys' + getAmountOut, + getLiquidityPoolKeysByLabel, +} from '@tools/sdk/raydium/helpers' +import { liquidityPoolKeysList } from '@tools/sdk/raydium/poolKeys' import { BN } from '@project-serum/anchor' +import BigNumber from 'bignumber.js' const AddLiquidityRaydium = ({ index, @@ -57,14 +54,14 @@ const AddLiquidityRaydium = ({ async function prepGovernances() { const mintWithGovernances = await getMintWithGovernances() const matchedGovernances = governancesArray.map((gov) => { - const governedTokenAccount = governedTokenAccounts.find( + const governedbaseccount = governedTokenAccounts.find( (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() ) const mintGovernance = mintWithGovernances.find( (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() ) - if (governedTokenAccount) { - return governedTokenAccount as GovernedMultiTypeAccount + if (governedbaseccount) { + return governedbaseccount as GovernedMultiTypeAccount } if (mintGovernance) { return mintGovernance as GovernedMultiTypeAccount @@ -82,10 +79,8 @@ const AddLiquidityRaydium = ({ const [form, setForm] = useState({ governedAccount: undefined, liquidityPool: '', - tokenAName: '', - tokenBName: '', - tokenAAmountIn: 0, - tokenBAmountIn: 0, + baseAmountIn: 0, + quoteAmountIn: 0, fixedSide: 'base', }) const [formErrors, setFormErrors] = useState({}) @@ -108,23 +103,20 @@ const AddLiquidityRaydium = ({ form.governedAccount?.governance?.account && wallet?.publicKey ) { - const tokenA = getGovernanceToken(connection.cluster, form.tokenAName) - const tokenB = getGovernanceToken(connection.cluster, form.tokenBName) - + const poolKeys = getLiquidityPoolKeysByLabel(form.liquidityPool) + const base = await connection.current.getTokenSupply(poolKeys.baseMint) + const quote = await connection.current.getTokenSupply(poolKeys.quoteMint) const createIx = createAddLiquidityInstruction( - new PublicKey(tokenA.address), - new PublicKey(tokenB.address), + poolKeys, new BN( - ( - Number(Number(form.tokenAAmountIn).toFixed(tokenA.decimals)) * - 10 ** tokenA.decimals - ).toString() + new BigNumber(form.baseAmountIn.toString()) + .shiftedBy(-base.value.decimals.toString()) + .toString() ), new BN( - ( - Number(Number(form.tokenBAmountIn).toFixed(tokenB.decimals)) * - 10 ** tokenB.decimals - ).toString() + new BigNumber(form.quoteAmountIn.toString()) + .shiftedBy(-quote.value.decimals.toString()) + .toString() ), form.fixedSide, form.governedAccount.governance.pubkey @@ -147,39 +139,28 @@ const AddLiquidityRaydium = ({ }, [realmInfo?.programId]) useEffect(() => { - if (form.tokenAName) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - // We are assuming for now that we only have one Liquidity Pool (UXP/USDC) - handleSetForm({ - value: getGovernanceMintSymbols(connection.cluster).filter( - (s) => s !== form.tokenAName - )[0], - propertyName: 'tokenBName', - }) - } - }, [form.tokenAName]) - - useEffect(() => { - if (form.tokenAAmountIn) { + if (form.baseAmountIn) { debounce.debounceFcn(async () => { handleSetForm({ value: await getAmountOut( - UXP_USDC_POOL_KEYS, - form.tokenAName, - Number(form.tokenAAmountIn), - form.tokenBName, + form.liquidityPool, + Number(form.baseAmountIn), connection ), - propertyName: 'tokenBAmountIn', + propertyName: 'quoteAmountIn', }) const { validationErrors } = await isFormValid(schema, form) setFormErrors(validationErrors) }) } - }, [form.tokenAAmountIn]) + }, [form.baseAmountIn]) + + useEffect(() => { + ;(async () => { + const { validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + })() + }, [form.quoteAmountIn]) useEffect(() => { handleSetInstructions( @@ -189,16 +170,15 @@ const AddLiquidityRaydium = ({ }, [form]) const schema = yup.object().shape({ - tokenAName: yup.string().required('Token A Name is required'), - tokenBName: yup.string().required('Token B Name is required'), - tokenAAmountIn: yup + liquidityPool: yup.string().required('Liquidity Pool is required'), + baseAmountIn: yup .number() - .moreThan(0, 'Amount for A token should be more than 0') - .required('Amount for A token is required'), - tokenBAmountIn: yup + .moreThan(0, 'Amount for Base token should be more than 0') + .required('Amount for Base token is required'), + quoteAmountIn: yup .number() - .moreThan(0, 'Amount for B token should be more than 0') - .required('Amount for B token is required'), + .moreThan(0, 'Amount for Quote token should be more than 0') + .required('Amount for Quote token is required'), governedAccount: yup .object() .nullable() @@ -232,90 +212,61 @@ const AddLiquidityRaydium = ({ } error={formErrors['liquidityPoolId']} > - {liquidityPoolList.map((pool, i) => ( - - {pool.label} - - ))} - - + {form.liquidityPool ? ( + <> + + handleSetForm({ + value: evt.target.value, + propertyName: 'baseAmountIn', + }) + } + error={formErrors['baseAmountIn']} + /> - - - - handleSetForm({ - value: evt.target.value, - propertyName: 'tokenAAmountIn', - }) - } - error={formErrors['tokenAAmountIn']} - /> - - - handleSetForm({ - value: Number(evt.target.value), - propertyName: 'tokenBAmountIn', - }) - } - disabled={true} - error={formErrors['tokenBAmountIn']} - /> - + + handleSetForm({ + value: Number(evt.target.value), + propertyName: 'quoteAmountIn', + }) + } + disabled={true} + error={formErrors['quoteAmountIn']} + /> + + + ) : null} ) } diff --git a/tools/sdk/raydium/createAddLiquidityInstruction.ts b/tools/sdk/raydium/createAddLiquidityInstruction.ts index 617481b81d..a12e4b8a33 100644 --- a/tools/sdk/raydium/createAddLiquidityInstruction.ts +++ b/tools/sdk/raydium/createAddLiquidityInstruction.ts @@ -1,33 +1,23 @@ import { BN } from '@project-serum/anchor' -import { AmountSide, Liquidity } from '@raydium-io/raydium-sdk' +import { + AmountSide, + Liquidity, + LiquidityPoolKeys, +} from '@raydium-io/raydium-sdk' import { TransactionInstruction, PublicKey } from '@solana/web3.js' import { findATAAddrSync } from '@uxdprotocol/uxd-client' -import { getLiquidityPoolKeysByAssets } from './helpers' export const createAddLiquidityInstruction = ( - tokenMintA: PublicKey, //TokenA we want to deposit (USDC or UXP) - tokenMintB: PublicKey, //Value in TokenB of TokenA (UXP or USDC) - amountA: BN, - amountB: BN, + poolKeys: LiquidityPoolKeys, + baseAmountIn: BN, + quoteAmountIn: BN, fixedSide: AmountSide, owner: PublicKey ): TransactionInstruction => { - const poolKeys = getLiquidityPoolKeysByAssets(tokenMintA, tokenMintB) - - if (!poolKeys) { - throw new Error('pool not found') - } const [lpTokenAccount] = findATAAddrSync(owner, poolKeys.lpMint) const [baseTokenAccount] = findATAAddrSync(owner, poolKeys.baseMint) const [quoteTokenAccount] = findATAAddrSync(owner, poolKeys.quoteMint) - let baseAmountIn = amountA - let quoteAmountIn = amountB - if (tokenMintA.equals(poolKeys.quoteMint)) { - baseAmountIn = amountB - quoteAmountIn = amountA - } - const itx = Liquidity.makeAddLiquidityInstruction({ poolKeys, userKeys: { diff --git a/tools/sdk/raydium/helpers.ts b/tools/sdk/raydium/helpers.ts index db27112ea7..a828c5ba93 100644 --- a/tools/sdk/raydium/helpers.ts +++ b/tools/sdk/raydium/helpers.ts @@ -9,23 +9,20 @@ import { import { PublicKey } from '@solana/web3.js' import { ConnectionContext } from '@utils/connection' import { findATAAddrSync } from '@uxdprotocol/uxd-client' -import { getGovernanceToken } from '../uxdProtocol/uxdClient' import { liquidityPoolKeys, liquidityPoolList } from './poolKeys' -export { liquidityPoolKeys } from './poolKeys' export const getAmountOut = async ( - poolKeys: LiquidityPoolKeys, - tokenNameIn: string, + liquidityPool: string, amountIn: number, - tokenNameOut: string, connection: ConnectionContext ) => { - const tokenInData = getGovernanceToken(connection.cluster, tokenNameIn) - const tokenOutData = getGovernanceToken(connection.cluster, tokenNameOut) + const poolKeys = getLiquidityPoolKeysByLabel(liquidityPool) + const base = await connection.current.getTokenSupply(poolKeys.baseMint) + const quote = await connection.current.getTokenSupply(poolKeys.quoteMint) const amountInBN = new BN( ( - Number(amountIn.toFixed(tokenInData.decimals)) * - 10 ** tokenInData.decimals + Number(amountIn.toFixed(base.value.decimals)) * + 10 ** base.value.decimals ).toString() ) const amountOut = Liquidity.computeCurrencyAmountOut({ @@ -35,16 +32,13 @@ export const getAmountOut = async ( poolKeys, }), currencyAmountIn: new TokenAmount( - new Token(new PublicKey(tokenInData.address), tokenInData.decimals), + new Token(poolKeys.baseMint, base.value.decimals), amountInBN ), - currencyOut: new Token( - new PublicKey(tokenOutData.address), - tokenOutData.decimals - ), + currencyOut: new Token(poolKeys.quoteMint, quote.value.decimals), slippage: new Percent(5, 1000), }) - const currentPrice = amountOut.currentPrice.toFixed(tokenOutData.decimals) + const currentPrice = amountOut.currentPrice.toFixed(quote.value.decimals) return Number(currentPrice) * amountIn } diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 9f8357540f..2df94bb970 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -75,10 +75,8 @@ export interface ProgramAuthorityForm { export interface AddLiquidityRaydiumForm { governedAccount: GovernedMultiTypeAccount | undefined liquidityPool: string - tokenAName: string - tokenBName: string - tokenAAmountIn: number - tokenBAmountIn: number + baseAmountIn: number + quoteAmountIn: number fixedSide: AmountSide } From 503b4f3df289e56aec97f77d6d98e1c16900019e Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 3 Feb 2022 16:44:10 +0900 Subject: [PATCH 095/204] fixes and improvements from comments --- .../instructions/raydium/AddLiquidity.tsx | 36 ++++++++++--------- .../instructions/raydium/RemoveLiquidity.tsx | 31 ++++++++-------- .../raydium/createAddLiquidityInstruction.ts | 4 +-- tools/sdk/raydium/helpers.ts | 26 +++++++------- 4 files changed, 51 insertions(+), 46 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx index f06a1c7da9..de44e47100 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx @@ -54,14 +54,14 @@ const AddLiquidityRaydium = ({ async function prepGovernances() { const mintWithGovernances = await getMintWithGovernances() const matchedGovernances = governancesArray.map((gov) => { - const governedbaseccount = governedTokenAccounts.find( + const governedBaseccount = governedTokenAccounts.find( (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() ) const mintGovernance = mintWithGovernances.find( (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() ) - if (governedbaseccount) { - return governedbaseccount as GovernedMultiTypeAccount + if (governedBaseccount) { + return governedBaseccount as GovernedMultiTypeAccount } if (mintGovernance) { return mintGovernance as GovernedMultiTypeAccount @@ -104,18 +104,21 @@ const AddLiquidityRaydium = ({ wallet?.publicKey ) { const poolKeys = getLiquidityPoolKeysByLabel(form.liquidityPool) - const base = await connection.current.getTokenSupply(poolKeys.baseMint) - const quote = await connection.current.getTokenSupply(poolKeys.quoteMint) + const [base, quote] = await Promise.all([ + connection.current.getTokenSupply(poolKeys.baseMint), + connection.current.getTokenSupply(poolKeys.quoteMint), + ]) + const createIx = createAddLiquidityInstruction( poolKeys, new BN( new BigNumber(form.baseAmountIn.toString()) - .shiftedBy(-base.value.decimals.toString()) + .shiftedBy(base.value.decimals) .toString() ), new BN( new BigNumber(form.quoteAmountIn.toString()) - .shiftedBy(-quote.value.decimals.toString()) + .shiftedBy(quote.value.decimals) .toString() ), form.fixedSide, @@ -144,7 +147,7 @@ const AddLiquidityRaydium = ({ handleSetForm({ value: await getAmountOut( form.liquidityPool, - Number(form.baseAmountIn), + form.baseAmountIn, connection ), propertyName: 'quoteAmountIn', @@ -156,10 +159,9 @@ const AddLiquidityRaydium = ({ }, [form.baseAmountIn]) useEffect(() => { - ;(async () => { - const { validationErrors } = await isFormValid(schema, form) + isFormValid(schema, form).then(({ validationErrors }) => { setFormErrors(validationErrors) - })() + }) }, [form.quoteAmountIn]) useEffect(() => { @@ -170,6 +172,10 @@ const AddLiquidityRaydium = ({ }, [form]) const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), liquidityPool: yup.string().required('Liquidity Pool is required'), baseAmountIn: yup .number() @@ -179,10 +185,6 @@ const AddLiquidityRaydium = ({ .number() .moreThan(0, 'Amount for Quote token should be more than 0') .required('Amount for Quote token is required'), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), fixedSide: yup .string() .equals(['base', 'quote']) @@ -210,9 +212,9 @@ const AddLiquidityRaydium = ({ onChange={(value) => handleSetForm({ value, propertyName: 'liquidityPool' }) } - error={formErrors['liquidityPoolId']} + error={formErrors['liquidityPool']} > - {[...Object.keys(liquidityPoolKeysList)].map((pool, i) => ( + {Object.keys(liquidityPoolKeysList).map((pool, i) => ( {pool} diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx index 5b54583174..72ddbfe077 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx @@ -37,7 +37,6 @@ const RemoveLiquidityRaydium = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - // const { getGovernancesByAccountType } = useGovernanceAssets() const [governedAccounts, setGovernedAccounts] = useState< GovernedMultiTypeAccount[] >([]) @@ -82,21 +81,22 @@ const RemoveLiquidityRaydium = ({ const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) - const [maxLPAmount, setMaxLPAmount] = useState(0) - const [LPDecimals, setLPDecimals] = useState(9) + const [lpMintInfo, setLpMintInfo] = useState<{ + balance: number + decimals: number + } | null>(null) useEffect(() => { const fetchLpData = async () => { if (!form.governedAccount?.governance.pubkey || !form.liquidityPool) return - const poolKeys = liquidityPoolKeysList[form.liquidityPool] + const { lpMint } = liquidityPoolKeysList[form.liquidityPool] const { maxBalance, decimals } = await getLPMintInfo( connection, - new PublicKey(poolKeys.lpMint), + new PublicKey(lpMint), form.governedAccount.governance.pubkey ) - setMaxLPAmount(maxBalance) - setLPDecimals(decimals) + setLpMintInfo({ balance: maxBalance, decimals }) } fetchLpData() }, [form.governedAccount?.governance.pubkey, form.liquidityPool]) @@ -118,12 +118,13 @@ const RemoveLiquidityRaydium = ({ programId && form.governedAccount?.governance?.account && form.liquidityPool && + lpMintInfo && wallet?.publicKey ) { const createIx = createRemoveLiquidityInstruction( new PublicKey(form.governedAccount.governance.pubkey), jsonInfo2PoolKeys(liquidityPoolKeysList[form.liquidityPool]), - new BigNumber(form.amountIn).shiftedBy(LPDecimals).toString() + new BigNumber(form.amountIn).shiftedBy(lpMintInfo.decimals).toString() ) serializedInstruction = serializeInstructionToBase64(createIx) } @@ -171,15 +172,15 @@ const RemoveLiquidityRaydium = ({ }, [form]) const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), liquidityPool: yup.string().required('Liquidity Pool is required'), amountIn: yup .number() .moreThan(0, 'Amount for LP token should be more than 0') .required('Amount for LP token is required'), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), }) return ( @@ -204,7 +205,7 @@ const RemoveLiquidityRaydium = ({ } error={formErrors['liquidityPool']} > - {[...Object.keys(liquidityPoolKeysList)].map((pool, i) => ( + {Object.keys(liquidityPoolKeysList).map((pool, i) => ( {pool} @@ -212,7 +213,9 @@ const RemoveLiquidityRaydium = ({ { const poolKeys = getLiquidityPoolKeysByLabel(liquidityPool) - const base = await connection.current.getTokenSupply(poolKeys.baseMint) - const quote = await connection.current.getTokenSupply(poolKeys.quoteMint) + const [base, quote] = await Promise.all([ + connection.current.getTokenSupply(poolKeys.baseMint), + connection.current.getTokenSupply(poolKeys.quoteMint), + ]) const amountInBN = new BN( - ( - Number(amountIn.toFixed(base.value.decimals)) * - 10 ** base.value.decimals - ).toString() + new BigNumber(Number(amountIn).toFixed(base.value.decimals)) + .shiftedBy(base.value.decimals) + .toString() ) const amountOut = Liquidity.computeCurrencyAmountOut({ poolKeys, @@ -49,10 +51,10 @@ export const getLPMintInfo = async ( user: PublicKey ) => { const [lpTokenAccount] = findATAAddrSync(user, lpMint) - const lpInfo = await connection.current.getTokenSupply(lpMint) - const lpUserBalance = await connection.current.getTokenAccountBalance( - lpTokenAccount - ) + const [lpInfo, lpUserBalance] = await Promise.all([ + connection.current.getTokenSupply(lpMint), + connection.current.getTokenAccountBalance(lpTokenAccount), + ]) return { lpTokenAccount, maxBalance: lpUserBalance.value.uiAmount ?? 0, @@ -75,10 +77,10 @@ export const getLiquidityPoolKeysByLabel = ( label: string ): LiquidityPoolKeys => { const lp = liquidityPoolList.find((lp) => lp.label === label)?.id - if (!lp) throw new Error('pool not found for label ' + label) + if (!lp) throw new Error(`pool not found for label ${label}`) const poolKeys = liquidityPoolKeys.find((lpk) => lpk.id.equals(lp)) - if (!poolKeys) throw new Error('pool not found for id ' + lp.toBase58()) + if (!poolKeys) throw new Error(`pool not found for id ${lp.toBase58()}`) return poolKeys } From 929949171ad051c492a06c7f0338bc0f7dba17eb Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 3 Feb 2022 17:07:23 +0900 Subject: [PATCH 096/204] cleanup and refacto helpers/poolKeys --- tools/sdk/raydium/helpers.ts | 21 ++-------- tools/sdk/raydium/poolKeys.ts | 72 +---------------------------------- 2 files changed, 5 insertions(+), 88 deletions(-) diff --git a/tools/sdk/raydium/helpers.ts b/tools/sdk/raydium/helpers.ts index 889e6853af..b1e01cc699 100644 --- a/tools/sdk/raydium/helpers.ts +++ b/tools/sdk/raydium/helpers.ts @@ -5,12 +5,13 @@ import { Percent, TokenAmount, Token, + jsonInfo2PoolKeys, } from '@raydium-io/raydium-sdk' import { PublicKey } from '@solana/web3.js' import { ConnectionContext } from '@utils/connection' import { findATAAddrSync } from '@uxdprotocol/uxd-client' import BigNumber from 'bignumber.js' -import { liquidityPoolKeys, liquidityPoolList } from './poolKeys' +import { liquidityPoolKeysList } from './poolKeys' export const getAmountOut = async ( liquidityPool: string, @@ -62,25 +63,11 @@ export const getLPMintInfo = async ( } } -export const getLiquidityPoolKeysByAssets = ( - base: PublicKey, - quote: PublicKey -): LiquidityPoolKeys | undefined => { - return liquidityPoolKeys.find( - (pk) => - (pk.baseMint.equals(base) && pk.quoteMint.equals(quote)) || - (pk.baseMint.equals(quote) && pk.quoteMint.equals(base)) - ) -} - export const getLiquidityPoolKeysByLabel = ( label: string ): LiquidityPoolKeys => { - const lp = liquidityPoolList.find((lp) => lp.label === label)?.id + const lp = Object.keys(liquidityPoolKeysList).find((lp) => lp === label) if (!lp) throw new Error(`pool not found for label ${label}`) - const poolKeys = liquidityPoolKeys.find((lpk) => lpk.id.equals(lp)) - if (!poolKeys) throw new Error(`pool not found for id ${lp.toBase58()}`) - - return poolKeys + return jsonInfo2PoolKeys(liquidityPoolKeysList[lp]) } diff --git a/tools/sdk/raydium/poolKeys.ts b/tools/sdk/raydium/poolKeys.ts index 7097d83379..357de2f94b 100644 --- a/tools/sdk/raydium/poolKeys.ts +++ b/tools/sdk/raydium/poolKeys.ts @@ -1,75 +1,5 @@ -import { LiquidityPoolKeys, jsonInfo2PoolKeys } from '@raydium-io/raydium-sdk' -import { PublicKey } from '@solana/web3.js' - -export const UXP_USDC_POOL_KEYS: LiquidityPoolKeys = jsonInfo2PoolKeys({ - id: '6tmFJbMk5yVHFcFy7X2K8RwHjKLr6KVFLYXpgpBNeAxB', - baseMint: 'UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M', - quoteMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - lpMint: 'AyuurXCCF2KdYTURbN3JsDKzZFZSiRqKc8UcZnrgBsGm', - version: 4, - programId: '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8', - authority: '5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1', - openOrders: '7AP5KPxkc9TYtYvqyXc4RK9GRVutGSne8Pj4ryKJoY4Z', - targetOrders: 'DfEhXNWDjsDNz1bqz6GinQU8RepjFneosamAM2XZ3heT', - baseVault: '3Dtb2kDA3pJkUrULXmQa8qn1RkmgnEM4eo2nf6Uuq3K3', - quoteVault: 'Gh2YaVC1sjzZQMixnHNXDin6awBAV6p2D5zY8STMu4p4', - withdrawQueue: '27BsfZSe59K2WXbhGGrvpySTRhd12moxxdLpYm6coFDT', - lpVault: '2dbkq546TV6C7Dmx5HWdHx7sTf6tpetvryqDRrcsE7kQ', - marketVersion: 3, - marketProgramId: '9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin', - marketId: '7KQpsp914VYnh62yV6AGfoG9hprfA14SgzEyqr6u9NY1', - marketAuthority: '5F4DUyyDR2uH7VTADLzi1CFmsVBVqPXk4TM4yHf9WDJi', - marketBaseVault: '9QGayBN3ycectkhLKiTPcfM9iFVtFpefSGWRr3XUoLwk', - marketQuoteVault: 'EiVf38NCvDFVJQqF5FgX1zeQ26Mzr88iELFugUSMJzu9', - marketBids: 'L6vnHnDLf8EPKXyaNAyhpkCdocvtkpNX8euVFZtqjCQ', - marketAsks: '68xmWKfE32qDoFL4iKBsjKpDyAfaiW4efdTDSAm33sKj', - marketEventQueue: 'Cta4TEwKCKhSphkMNzXsURVr2V6mozm2SPaV8tCgDPwy', -}) - -// fetch poolkeys from https://sdk.raydium.io/liquidity/mainnet.json -const poolKeys = [ - { - /** - * ============= UXP/USDC ============= - */ - id: '6tmFJbMk5yVHFcFy7X2K8RwHjKLr6KVFLYXpgpBNeAxB', - baseMint: 'UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M', - quoteMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', - lpMint: 'AyuurXCCF2KdYTURbN3JsDKzZFZSiRqKc8UcZnrgBsGm', - version: 4, - programId: '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8', - authority: '5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1', - openOrders: '7AP5KPxkc9TYtYvqyXc4RK9GRVutGSne8Pj4ryKJoY4Z', - targetOrders: 'DfEhXNWDjsDNz1bqz6GinQU8RepjFneosamAM2XZ3heT', - baseVault: '3Dtb2kDA3pJkUrULXmQa8qn1RkmgnEM4eo2nf6Uuq3K3', - quoteVault: 'Gh2YaVC1sjzZQMixnHNXDin6awBAV6p2D5zY8STMu4p4', - withdrawQueue: '27BsfZSe59K2WXbhGGrvpySTRhd12moxxdLpYm6coFDT', - lpVault: '2dbkq546TV6C7Dmx5HWdHx7sTf6tpetvryqDRrcsE7kQ', - marketVersion: 3, - marketProgramId: '9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin', - marketId: '7KQpsp914VYnh62yV6AGfoG9hprfA14SgzEyqr6u9NY1', - marketAuthority: '5F4DUyyDR2uH7VTADLzi1CFmsVBVqPXk4TM4yHf9WDJi', - marketBaseVault: '9QGayBN3ycectkhLKiTPcfM9iFVtFpefSGWRr3XUoLwk', - marketQuoteVault: 'EiVf38NCvDFVJQqF5FgX1zeQ26Mzr88iELFugUSMJzu9', - marketBids: 'L6vnHnDLf8EPKXyaNAyhpkCdocvtkpNX8euVFZtqjCQ', - marketAsks: '68xmWKfE32qDoFL4iKBsjKpDyAfaiW4efdTDSAm33sKj', - marketEventQueue: 'Cta4TEwKCKhSphkMNzXsURVr2V6mozm2SPaV8tCgDPwy', - }, - // Add other Raydium liquidity pools here -] - -export const liquidityPoolKeys: LiquidityPoolKeys[] = poolKeys.map((pk) => - jsonInfo2PoolKeys(pk) -) - -export const liquidityPoolList = [ - { - label: 'UXP - USDC', - id: new PublicKey('6tmFJbMk5yVHFcFy7X2K8RwHjKLr6KVFLYXpgpBNeAxB'), - }, -] - export const liquidityPoolKeysList = { + // fetch poolkeys from https://sdk.raydium.io/liquidity/mainnet.json 'UXP - USDC': { id: '6tmFJbMk5yVHFcFy7X2K8RwHjKLr6KVFLYXpgpBNeAxB', baseMint: 'UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M', From 4a602bde2d5c6358904997a122d06346d967ccef Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 3 Feb 2022 22:02:58 +0900 Subject: [PATCH 097/204] refacto getMultiTypeAccounts for raydium itxs --- .../instructions/raydium/AddLiquidity.tsx | 29 ++----------------- .../instructions/raydium/RemoveLiquidity.tsx | 29 ++----------------- 2 files changed, 4 insertions(+), 54 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx index de44e47100..913762ea9f 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx @@ -44,35 +44,10 @@ const AddLiquidityRaydium = ({ const [governedAccounts, setGovernedAccounts] = useState< GovernedMultiTypeAccount[] >([]) - const { - governancesArray, - governedTokenAccounts, - getMintWithGovernances, - } = useGovernanceAssets() + const { getGovernedMultiTypeAccounts } = useGovernanceAssets() useEffect(() => { - async function prepGovernances() { - const mintWithGovernances = await getMintWithGovernances() - const matchedGovernances = governancesArray.map((gov) => { - const governedBaseccount = governedTokenAccounts.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() - ) - const mintGovernance = mintWithGovernances.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() - ) - if (governedBaseccount) { - return governedBaseccount as GovernedMultiTypeAccount - } - if (mintGovernance) { - return mintGovernance as GovernedMultiTypeAccount - } - return { - governance: gov, - } - }) - setGovernedAccounts(matchedGovernances) - } - prepGovernances() + getGovernedMultiTypeAccounts().then(setGovernedAccounts) }, []) const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx index 72ddbfe077..38151b34e0 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx @@ -40,35 +40,10 @@ const RemoveLiquidityRaydium = ({ const [governedAccounts, setGovernedAccounts] = useState< GovernedMultiTypeAccount[] >([]) - const { - governancesArray, - governedTokenAccounts, - getMintWithGovernances, - } = useGovernanceAssets() + const { getGovernedMultiTypeAccounts } = useGovernanceAssets() useEffect(() => { - async function prepGovernances() { - const mintWithGovernances = await getMintWithGovernances() - const matchedGovernances = governancesArray.map((gov) => { - const governedTokenAccount = governedTokenAccounts.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() - ) - const mintGovernance = mintWithGovernances.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() - ) - if (governedTokenAccount) { - return governedTokenAccount as GovernedMultiTypeAccount - } - if (mintGovernance) { - return mintGovernance as GovernedMultiTypeAccount - } - return { - governance: gov, - } - }) - setGovernedAccounts(matchedGovernances) - } - prepGovernances() + getGovernedMultiTypeAccounts().then(setGovernedAccounts) }, []) const shouldBeGoverned = index !== 0 && governance From 55e4b4dd1db5c0f0f4c5541843c5ac00c855f8b8 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sat, 5 Feb 2022 11:37:31 +0900 Subject: [PATCH 098/204] update lock file --- yarn.lock | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/yarn.lock b/yarn.lock index 61c689f947..8751ee497e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -568,6 +568,22 @@ version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" +"@blockworks-foundation/mango-client@3.2.15": + version "3.2.15" + resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.2.15.tgz#2b9fdb04068a420af6a8f60c42ca65a53e20a1bc" + integrity sha512-oyTMgQ7t6CjUIfJ26RygLg1eSu4zmMypD9iPWAglzFqJkmrwc0HOrr309ylEeUT1P5PxUxl3E3AaxjnhzLjMRw== + dependencies: + "@project-serum/anchor" "^0.16.2" + "@project-serum/serum" "0.13.55" + "@project-serum/sol-wallet-adapter" "^0.2.0" + "@solana/spl-token" "^0.1.6" + "@solana/web3.js" "1.21.0" + big.js "^6.1.1" + bn.js "^5.2.0" + buffer-layout "^1.2.1" + dotenv "^10.0.0" + yargs "^17.0.1" + "@blockworks-foundation/mango-client@^3.3.12": version "3.3.12" resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.3.12.tgz#da69dbc7d89b10a95a6771c7a1a4b452172eef83" @@ -1889,6 +1905,26 @@ "@solana/wallet-adapter-base" "^0.9.1" "@solana/web3.js" "^1.20.0" +"@solana/web3.js@1.21.0": + version "1.21.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.21.0.tgz#4f98edea38d4cb3ae4d2ea49a050b0ec09508023" + integrity sha512-x1NXlF92tEjxuTxS0u4n9JV17UKk0Dn2L+qSWGvKOb4iWhzApDj6wicJsrGdSbGdxnZ7eciQ/SNn3zB4ydUllA== + dependencies: + "@babel/runtime" "^7.12.5" + "@solana/buffer-layout" "^3.0.0" + bn.js "^5.0.0" + borsh "^0.4.0" + bs58 "^4.0.1" + buffer "6.0.1" + crypto-hash "^1.2.2" + jayson "^3.4.4" + js-sha3 "^0.8.0" + node-fetch "^2.6.1" + rpc-websockets "^7.4.2" + secp256k1 "^4.0.2" + superstruct "^0.14.2" + tweetnacl "^1.0.0" + "@solana/web3.js@^1.12.0", "@solana/web3.js@^1.31.0": version "1.31.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.31.0.tgz#7a313d4c1a90b77f27ddbfe845a10d6883e06452" From 88f1b1369ccf5860cb05522f394a6f01131d0536 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sat, 5 Feb 2022 18:59:46 +0900 Subject: [PATCH 099/204] remove uxd env --- .env | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index 15f8152331..0000000000 --- a/.env +++ /dev/null @@ -1,6 +0,0 @@ -# Run in context of one realm: starts with default realm and disables realms page navigation -REALM=UXP -MAINNET_RPC=https://billowing-proud-glitter.solana-mainnet.quiknode.pro/7f49d7f436c2d3af0738270d90dee86962f13a82/ -DEVNET_RPC=https://api.devnet.solana.com/ - - From 6184d33b358ea8d378ebbd74bf1540ac1cfb8132 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 6 Feb 2022 15:49:33 +0900 Subject: [PATCH 100/204] smol fix --- .../instructions/raydium/RemoveLiquidity.tsx | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx index 38151b34e0..1b1934fb60 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx @@ -26,6 +26,7 @@ import { liquidityPoolKeysList } from '@tools/sdk/raydium/poolKeys' import { createRemoveLiquidityInstruction } from '@tools/sdk/raydium/createRemoveLiquidityInstruction' import BigNumber from 'bignumber.js' import { jsonInfo2PoolKeys } from '@raydium-io/raydium-sdk' +import { notify } from '@utils/notifications' const RemoveLiquidityRaydium = ({ index, @@ -65,13 +66,20 @@ const RemoveLiquidityRaydium = ({ if (!form.governedAccount?.governance.pubkey || !form.liquidityPool) return const { lpMint } = liquidityPoolKeysList[form.liquidityPool] - - const { maxBalance, decimals } = await getLPMintInfo( - connection, - new PublicKey(lpMint), - form.governedAccount.governance.pubkey - ) - setLpMintInfo({ balance: maxBalance, decimals }) + try { + const { maxBalance, decimals } = await getLPMintInfo( + connection, + new PublicKey(lpMint), + form.governedAccount.governance.pubkey + ) + setLpMintInfo({ balance: maxBalance, decimals }) + } catch (e) { + notify({ + type: 'error', + message: 'Could not fetch LP Account', + description: `${form.liquidityPool} LP Token Account could not be found for the selected Governance`, + }) + } } fetchLpData() }, [form.governedAccount?.governance.pubkey, form.liquidityPool]) From 8996f4176470cec7a709f8d8b8459be86f667cfc Mon Sep 17 00:00:00 2001 From: GregoryNEUT Date: Mon, 7 Feb 2022 17:22:42 +0100 Subject: [PATCH 101/204] Fix governance select (#33) * fix governance select --- hooks/useGovernanceAssets.ts | 29 +------- hooks/useGovernedMultiTypeAccounts.ts | 68 +++++++++++++++++++ .../CreateAssociatedTokenAccount.tsx | 14 +--- .../components/instructions/CustomBase64.tsx | 14 +--- .../components/instructions/Empty.tsx | 14 +--- .../instructions/raydium/AddLiquidity.tsx | 14 +--- .../instructions/raydium/RemoveLiquidity.tsx | 14 +--- .../solend/CreateObligationAccount.tsx | 14 +--- ...eserveLiquidityAndObligationCollateral.tsx | 14 +--- .../solend/InitObligationAccount.tsx | 15 +--- .../instructions/solend/RefreshObligation.tsx | 14 +--- ...ionCollateralAndRedeemReserveLiquidity.tsx | 15 +--- 12 files changed, 99 insertions(+), 140 deletions(-) create mode 100644 hooks/useGovernedMultiTypeAccounts.ts diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index e0e9a95b58..cd67167880 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -13,7 +13,6 @@ import { AccountInfoGen, getMultipleAccountInfoChunked, GovernedMintInfoAccount, - GovernedMultiTypeAccount, GovernedTokenAccount, parseMintAccountData, } from '@utils/tokens' @@ -153,32 +152,6 @@ export default function useGovernanceAssets() { ownVoterWeight.canCreateProposal(g.governance?.account?.config) ) - const getGovernedMultiTypeAccounts = async (): Promise< - GovernedMultiTypeAccount[] - > => { - const mintWithGovernances = await getMintWithGovernances() - - return governancesArray.map((gov) => { - const governedTokenAccount = governedTokenAccounts.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() - ) - if (governedTokenAccount) { - return governedTokenAccount as GovernedMultiTypeAccount - } - - const mintGovernance = mintWithGovernances.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() - ) - if (mintGovernance) { - return mintGovernance as GovernedMultiTypeAccount - } - - return { - governance: gov, - } - }) - } - const availableInstructions = [ { id: Instructions.CreateAssociatedTokenAccount, @@ -312,6 +285,7 @@ export default function useGovernanceAssets() { useEffect(() => { async function prepareTokenGovernances() { const governedTokenAccountsArray: GovernedTokenAccount[] = [] + for (const gov of tokenGovernances) { const realmTokenAccount = realmTokenAccounts.find( (x) => @@ -373,7 +347,6 @@ export default function useGovernanceAssets() { ]) return { governancesArray, - getGovernedMultiTypeAccounts, getGovernancesByAccountType, getGovernancesByAccountTypes, availableInstructions, diff --git a/hooks/useGovernedMultiTypeAccounts.ts b/hooks/useGovernedMultiTypeAccounts.ts new file mode 100644 index 0000000000..1af34eba58 --- /dev/null +++ b/hooks/useGovernedMultiTypeAccounts.ts @@ -0,0 +1,68 @@ +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { useCallback, useEffect, useState } from 'react' +import useGovernanceAssets from './useGovernanceAssets' + +export default function useGovernedMultiTypeAccounts() { + const { + getMintWithGovernances, + governancesArray, + governedTokenAccounts, + } = useGovernanceAssets() + + const [governedMultiTypeAccounts, setGovernedMultiTypeAccounts] = useState< + GovernedMultiTypeAccount[] + >([]) + + const getGovernedMultiTypeAccounts = useCallback(async (): Promise< + GovernedMultiTypeAccount[] + > => { + const mintWithGovernances = await getMintWithGovernances() + + return governancesArray.map((gov) => { + const governedTokenAccount = governedTokenAccounts.find( + (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() + ) + if (governedTokenAccount) { + return governedTokenAccount as GovernedMultiTypeAccount + } + + const mintGovernance = mintWithGovernances.find( + (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() + ) + if (mintGovernance) { + return mintGovernance as GovernedMultiTypeAccount + } + + return { + governance: gov, + } + }) + + // FIXME: `governedTokenAccounts` & `governancesArray` should have stable references. + // These should respect immutability principles & only change if their content changes. + // Working around this by stringifying both objects & using the resulting string + // representation as hook dependency, so the hook only runs when either of these changes, + // but with a performance tax unfortunately + }, [JSON.stringify(governedTokenAccounts), JSON.stringify(governancesArray)]) + + useEffect(() => { + // Ignore obsolete results created by race calls + let abort = false + + ;(async () => { + const governedMultiTypeAccounts = await getGovernedMultiTypeAccounts() + + if (abort) return + + setGovernedMultiTypeAccounts(governedMultiTypeAccounts) + })() + + return () => { + abort = true + } + }, [getGovernedMultiTypeAccounts]) + + return { + governedMultiTypeAccounts, + } +} diff --git a/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx index 4805a04a23..41f95b7353 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx @@ -9,9 +9,7 @@ import { CreateAssociatedTokenAccountForm, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' -import { GovernedMultiTypeAccount } from '@utils/tokens' import Select from '@components/inputs/Select' import { ProgramAccount, @@ -21,6 +19,7 @@ import { import GovernedAccountSelect from '../GovernedAccountSelect' import { createAssociatedTokenAccount } from '@utils/associated' import { getSplTokenMintAddressByUIName, SPL_TOKENS } from '@utils/splTokens' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const CreateAssociatedTokenAccount = ({ index, @@ -32,14 +31,7 @@ const CreateAssociatedTokenAccount = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const [governedAccounts, setGovernedAccounts] = useState< - GovernedMultiTypeAccount[] - >([]) - const { getGovernedMultiTypeAccounts } = useGovernanceAssets() - - useEffect(() => { - getGovernedMultiTypeAccounts().then(setGovernedAccounts) - }, []) + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId @@ -136,7 +128,7 @@ const CreateAssociatedTokenAccount = ({ <> { handleSetForm({ value, propertyName: 'governedAccount' }) }} diff --git a/pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx b/pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx index e3773ce8de..025a796fe7 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx @@ -5,16 +5,15 @@ import { UiInstruction, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' import { Governance } from '@solana/spl-governance' import { ProgramAccount } from '@solana/spl-governance' import useWalletStore from 'stores/useWalletStore' import GovernedAccountSelect from '../GovernedAccountSelect' -import { GovernedMultiTypeAccount } from '@utils/tokens' import Input from '@components/inputs/Input' import Textarea from '@components/inputs/Textarea' import { getInstructionDataFromBase64 } from '@solana/spl-governance' import { validateInstruction } from '@utils/instructionTools' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const CustomBase64 = ({ index, @@ -24,11 +23,8 @@ const CustomBase64 = ({ governance: ProgramAccount | null }) => { const wallet = useWalletStore((s) => s.current) - const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() const shouldBeGoverned = index !== 0 && governance - const [governedAccounts, setGovernedAccounts] = useState< - GovernedMultiTypeAccount[] - >([]) const [form, setForm] = useState({ governedAccount: undefined, base64: '', @@ -41,10 +37,6 @@ const CustomBase64 = ({ setForm({ ...form, [propertyName]: value }) } - useEffect(() => { - getGovernedMultiTypeAccounts().then(setGovernedAccounts) - }, []) - async function getInstruction(): Promise { const isValid = await validateInstruction({ schema, form, setFormErrors }) let serializedInstruction = '' @@ -109,7 +101,7 @@ const CustomBase64 = ({ <> { handleSetForm({ value, propertyName: 'governedAccount' }) }} diff --git a/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx b/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx index bb67834df0..cdb5f9a433 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx @@ -5,12 +5,11 @@ import { UiInstruction, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' import { Governance } from '@solana/spl-governance' import { ProgramAccount } from '@solana/spl-governance' import GovernedAccountSelect from '../GovernedAccountSelect' -import { GovernedMultiTypeAccount } from '@utils/tokens' import { validateInstruction } from '@utils/instructionTools' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const Empty = ({ index, @@ -19,11 +18,8 @@ const Empty = ({ index: number governance: ProgramAccount | null }) => { - const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() const shouldBeGoverned = index !== 0 && governance - const [governedAccounts, setGovernedAccounts] = useState< - GovernedMultiTypeAccount[] - >([]) const [form, setForm] = useState({ governedAccount: undefined, }) @@ -34,10 +30,6 @@ const Empty = ({ setForm({ ...form, [propertyName]: value }) } - useEffect(() => { - getGovernedMultiTypeAccounts().then(setGovernedAccounts) - }, []) - async function getInstruction(): Promise { const isValid = await validateInstruction({ schema, form, setFormErrors }) const obj: UiInstruction = { @@ -64,7 +56,7 @@ const Empty = ({ <> { handleSetForm({ value, propertyName: 'governedAccount' }) }} diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx index 913762ea9f..7239061a09 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx @@ -9,11 +9,9 @@ import { AddLiquidityRaydiumForm, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' -import { GovernedMultiTypeAccount } from '@utils/tokens' import Select from '@components/inputs/Select' import { ProgramAccount, @@ -29,6 +27,7 @@ import { import { liquidityPoolKeysList } from '@tools/sdk/raydium/poolKeys' import { BN } from '@project-serum/anchor' import BigNumber from 'bignumber.js' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const AddLiquidityRaydium = ({ index, @@ -40,15 +39,8 @@ const AddLiquidityRaydium = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - // const { getGovernancesByAccountType } = useGovernanceAssets() - const [governedAccounts, setGovernedAccounts] = useState< - GovernedMultiTypeAccount[] - >([]) - const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - useEffect(() => { - getGovernedMultiTypeAccounts().then(setGovernedAccounts) - }, []) const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({ @@ -170,7 +162,7 @@ const AddLiquidityRaydium = ({ <> { handleSetForm({ value, propertyName: 'governedAccount' }) }} diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx index 1b1934fb60..d873df2fde 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx @@ -9,11 +9,9 @@ import { UiInstruction, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' import Input from '@components/inputs/Input' import { debounce } from '@utils/debounce' -import { GovernedMultiTypeAccount } from '@utils/tokens' import Select from '@components/inputs/Select' import { ProgramAccount, @@ -27,6 +25,7 @@ import { createRemoveLiquidityInstruction } from '@tools/sdk/raydium/createRemov import BigNumber from 'bignumber.js' import { jsonInfo2PoolKeys } from '@raydium-io/raydium-sdk' import { notify } from '@utils/notifications' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const RemoveLiquidityRaydium = ({ index, @@ -38,14 +37,7 @@ const RemoveLiquidityRaydium = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const [governedAccounts, setGovernedAccounts] = useState< - GovernedMultiTypeAccount[] - >([]) - const { getGovernedMultiTypeAccounts } = useGovernanceAssets() - - useEffect(() => { - getGovernedMultiTypeAccounts().then(setGovernedAccounts) - }, []) + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId @@ -170,7 +162,7 @@ const RemoveLiquidityRaydium = ({ <> { handleSetForm({ value, propertyName: 'governedAccount' }) }} diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx index 0dead0951d..fce5942933 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx @@ -9,9 +9,7 @@ import { CreateSolendObligationAccountForm, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' -import { GovernedMultiTypeAccount } from '@utils/tokens' import { ProgramAccount, serializeInstructionToBase64, @@ -19,6 +17,7 @@ import { } from '@solana/spl-governance' import GovernedAccountSelect from '../../GovernedAccountSelect' import { createObligationAccount } from '@tools/sdk/solend/createObligationAccount' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const CreateObligationAccount = ({ index, @@ -30,20 +29,13 @@ const CreateObligationAccount = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const [governedAccounts, setGovernedAccounts] = useState< - GovernedMultiTypeAccount[] - >([]) - const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} } - useEffect(() => { - getGovernedMultiTypeAccounts().then(setGovernedAccounts) - }, []) - const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({}) @@ -118,7 +110,7 @@ const CreateObligationAccount = ({ <> { handleSetForm({ value, propertyName: 'governedAccount' }) }} diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx index 732c8c7862..cf8ef1d293 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx @@ -9,9 +9,7 @@ import { DepositReserveLiquidityAndObligationCollateralForm, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' -import { GovernedMultiTypeAccount } from '@utils/tokens' import { ProgramAccount, serializeInstructionToBase64, @@ -26,6 +24,7 @@ import SolendConfiguration from '@tools/sdk/solend/configuration' import BigNumber from 'bignumber.js' import { BN } from '@project-serum/anchor' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const DepositReserveLiquidityAndObligationCollateral = ({ index, @@ -37,20 +36,13 @@ const DepositReserveLiquidityAndObligationCollateral = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const [governedAccounts, setGovernedAccounts] = useState< - GovernedMultiTypeAccount[] - >([]) - const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} } - useEffect(() => { - getGovernedMultiTypeAccounts().then(setGovernedAccounts) - }, []) - const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId const [ @@ -144,7 +136,7 @@ const DepositReserveLiquidityAndObligationCollateral = ({ <> { handleSetForm({ value, propertyName: 'governedAccount' }) }} diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/InitObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/InitObligationAccount.tsx index 6dd59f94b9..a001094016 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/InitObligationAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/InitObligationAccount.tsx @@ -9,9 +9,7 @@ import { InitSolendObligationAccountForm, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' -import { GovernedMultiTypeAccount } from '@utils/tokens' import { ProgramAccount, serializeInstructionToBase64, @@ -19,6 +17,7 @@ import { } from '@solana/spl-governance' import GovernedAccountSelect from '../../GovernedAccountSelect' import { initObligationAccount } from '@tools/sdk/solend/initObligationAccount' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const InitObligationAccount = ({ index, @@ -30,21 +29,13 @@ const InitObligationAccount = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const [governedAccounts, setGovernedAccounts] = useState< - GovernedMultiTypeAccount[] - >([]) - - const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} } - useEffect(() => { - getGovernedMultiTypeAccounts().then(setGovernedAccounts) - }, []) - const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({}) @@ -118,7 +109,7 @@ const InitObligationAccount = ({ <> { handleSetForm({ value, propertyName: 'governedAccount' }) }} diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshObligation.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshObligation.tsx index 6c92890a3c..736964cbb3 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshObligation.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshObligation.tsx @@ -9,9 +9,7 @@ import { RefreshObligationForm, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' -import { GovernedMultiTypeAccount } from '@utils/tokens' import { ProgramAccount, serializeInstructionToBase64, @@ -21,6 +19,7 @@ import GovernedAccountSelect from '../../GovernedAccountSelect' import Select from '@components/inputs/Select' import SolendConfiguration from '@tools/sdk/solend/configuration' import { refreshObligation } from '@tools/sdk/solend/refreshObligation' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const RefreshObligation = ({ index, @@ -32,20 +31,13 @@ const RefreshObligation = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const [governedAccounts, setGovernedAccounts] = useState< - GovernedMultiTypeAccount[] - >([]) - const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} } - useEffect(() => { - getGovernedMultiTypeAccounts().then(setGovernedAccounts) - }, []) - const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({}) @@ -122,7 +114,7 @@ const RefreshObligation = ({ <> { handleSetForm({ value, propertyName: 'governedAccount' }) }} diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx index 4113fb0756..370157db2a 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx @@ -9,9 +9,7 @@ import { WithdrawObligationCollateralAndRedeemReserveLiquidityForm, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' import useWalletStore from 'stores/useWalletStore' -import { GovernedMultiTypeAccount } from '@utils/tokens' import { ProgramAccount, serializeInstructionToBase64, @@ -24,6 +22,7 @@ import SolendConfiguration from '@tools/sdk/solend/configuration' import { withdrawObligationCollateralAndRedeemReserveLiquidity } from '@tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity' import BigNumber from 'bignumber.js' import { BN } from '@project-serum/anchor' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({ index, @@ -35,21 +34,13 @@ const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const [governedAccounts, setGovernedAccounts] = useState< - GovernedMultiTypeAccount[] - >([]) - - const { getGovernedMultiTypeAccounts } = useGovernanceAssets() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} } - useEffect(() => { - getGovernedMultiTypeAccounts().then(setGovernedAccounts) - }, []) - const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId const [ @@ -143,7 +134,7 @@ const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({ <> { handleSetForm({ value, propertyName: 'governedAccount' }) }} From 3951f0fee6ca968f96e939e14525e799828341c8 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 13 Feb 2022 13:42:06 +0900 Subject: [PATCH 102/204] udpate Wallet import --- components/instructions/programs/voteStakeRegistry.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/instructions/programs/voteStakeRegistry.tsx b/components/instructions/programs/voteStakeRegistry.tsx index d6b0cb9892..2aca442035 100644 --- a/components/instructions/programs/voteStakeRegistry.tsx +++ b/components/instructions/programs/voteStakeRegistry.tsx @@ -1,5 +1,6 @@ import { VsrClient } from '@blockworks-foundation/voter-stake-registry-client' -import { BN, Provider, Wallet } from '@project-serum/anchor' +import { BN, Provider } from '@project-serum/anchor' +import { Wallet } from '@project-serum/sol-wallet-adapter' import { AccountMetaData } from '@solana/spl-governance' import { Connection, Keypair, PublicKey } from '@solana/web3.js' import { fmtMintAmount } from '@tools/sdk/units' From ce4ca1ded061e3f0eaef68fb6ff65c4db823a719 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 13 Feb 2022 14:03:41 +0900 Subject: [PATCH 103/204] update lock file --- yarn.lock | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/yarn.lock b/yarn.lock index 894d9a0afd..0eb6004e27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -568,6 +568,22 @@ version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" +"@blockworks-foundation/mango-client@3.2.15": + version "3.2.15" + resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.2.15.tgz#2b9fdb04068a420af6a8f60c42ca65a53e20a1bc" + integrity sha512-oyTMgQ7t6CjUIfJ26RygLg1eSu4zmMypD9iPWAglzFqJkmrwc0HOrr309ylEeUT1P5PxUxl3E3AaxjnhzLjMRw== + dependencies: + "@project-serum/anchor" "^0.16.2" + "@project-serum/serum" "0.13.55" + "@project-serum/sol-wallet-adapter" "^0.2.0" + "@solana/spl-token" "^0.1.6" + "@solana/web3.js" "1.21.0" + big.js "^6.1.1" + bn.js "^5.2.0" + buffer-layout "^1.2.1" + dotenv "^10.0.0" + yargs "^17.0.1" + "@blockworks-foundation/mango-client@^3.3.15": version "3.3.15" resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.3.15.tgz#f08c849780357a09e727d86964ebc50b472225fa" From 91873efdf9e021d339bc7feb019b0dddc2e0ec71 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 13 Feb 2022 14:04:25 +0900 Subject: [PATCH 104/204] update Wallet Interface import --- VoteStakeRegistry/stores/voteStakeRegistryClientStore.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/VoteStakeRegistry/stores/voteStakeRegistryClientStore.tsx b/VoteStakeRegistry/stores/voteStakeRegistryClientStore.tsx index c298d04c07..2aa2795c5a 100644 --- a/VoteStakeRegistry/stores/voteStakeRegistryClientStore.tsx +++ b/VoteStakeRegistry/stores/voteStakeRegistryClientStore.tsx @@ -1,7 +1,8 @@ import create, { State } from 'zustand' import { VsrClient } from '@blockworks-foundation/voter-stake-registry-client' import { getRegistrarPDA, Registrar } from 'VoteStakeRegistry/sdk/accounts' -import { Provider, Wallet } from '@project-serum/anchor' +import { Provider } from '@project-serum/anchor' +import { Wallet } from '@project-serum/sol-wallet-adapter' import { tryGetRegistrar } from 'VoteStakeRegistry/sdk/api' import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import { ConnectionContext } from '@utils/connection' From 7f8ca0f50848a4015ae97f470d6e5f9230538a4f Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sat, 19 Feb 2022 13:34:02 +0900 Subject: [PATCH 105/204] add link to tx explorer once proposal is executed (#39) * add link to tx explorer once proposal is executed * use itx[0] instead of proposal address * refacto to fetch signature for each itx - wip * kinda works for single and multi itx proposals * remove noopener * remove retry, rpc call is failing randomly --- .../instructions/ExecuteInstructionButton.tsx | 20 +++++++++--- components/instructions/instructionCard.tsx | 1 + components/instructions/instructionPanel.tsx | 24 +++++++------- hooks/useTransactionSignature.ts | 31 +++++++++++++++++++ pages/dao/[symbol]/proposal/[pk].tsx | 24 +++++++------- 5 files changed, 73 insertions(+), 27 deletions(-) create mode 100644 hooks/useTransactionSignature.ts diff --git a/components/instructions/ExecuteInstructionButton.tsx b/components/instructions/ExecuteInstructionButton.tsx index fd64b65ddc..7e3a9ecaad 100644 --- a/components/instructions/ExecuteInstructionButton.tsx +++ b/components/instructions/ExecuteInstructionButton.tsx @@ -16,6 +16,7 @@ import { ProgramAccount } from '@solana/spl-governance' import { PublicKey } from '@solana/web3.js' import Tooltip from '@components/Tooltip' import { getProgramVersionForRealm } from '@models/registry/api' +import useTransactionSignature from '@hooks/useTransactionSignature' export enum PlayState { Played, @@ -40,9 +41,10 @@ export function ExecuteInstructionButton({ const connection = useWalletStore((s) => s.connection) const fetchRealm = useWalletStore((s) => s.actions.fetchRealm) const connected = useWalletStore((s) => s.connected) - const [currentSlot, setCurrentSlot] = useState(0) - + const { transactionSignature } = useTransactionSignature( + proposalInstruction.pubkey + ) const canExecuteAt = proposal?.account.votingCompletedAt ? proposal.account.votingCompletedAt.toNumber() + 1 : 0 @@ -85,14 +87,24 @@ export function ExecuteInstructionButton({ setPlaying(PlayState.Played) } - if ( proposalInstruction.account.executionStatus === InstructionExecutionStatus.Success ) { return ( - + {transactionSignature ? ( +
e.stopPropagation()} + > + + + ) : ( + + )} ) } diff --git a/components/instructions/instructionCard.tsx b/components/instructions/instructionCard.tsx index 096ead10e0..4595ac30a0 100644 --- a/components/instructions/instructionCard.tsx +++ b/components/instructions/instructionCard.tsx @@ -49,6 +49,7 @@ export default function InstructionCard({ ) const [nftImgUrl, setNftImgUrl] = useState('') const [tokenImgUrl, setTokenImgUrl] = useState('') + useEffect(() => { getInstructionDescriptor( connection.current, diff --git a/components/instructions/instructionPanel.tsx b/components/instructions/instructionPanel.tsx index 0a7f104e59..0b8ca7d5be 100644 --- a/components/instructions/instructionPanel.tsx +++ b/components/instructions/instructionPanel.tsx @@ -92,17 +92,19 @@ export function InstructionPanel() { - {proposalInstructions.map((pi, idx) => ( -
- {proposal && ( - - )} -
- ))} + {proposalInstructions.map((pi, idx) => { + return ( +
+ {proposal && ( + + )} +
+ ) + })} {proposal && (
diff --git a/hooks/useTransactionSignature.ts b/hooks/useTransactionSignature.ts new file mode 100644 index 0000000000..13fa15e637 --- /dev/null +++ b/hooks/useTransactionSignature.ts @@ -0,0 +1,31 @@ +import { PublicKey } from '@solana/web3.js' +import { useState, useEffect } from 'react' +import useWalletStore from 'stores/useWalletStore' + +const useTransactionSignature = (address?: PublicKey) => { + const connection = useWalletStore((s) => s.connection) + const [transactionSignature, setTransactionSignature] = useState('') + useEffect(() => { + async function getSignature() { + if (!address) return + const transactions = await connection.current.getConfirmedSignaturesForAddress2( + address, + { limit: 1 }, + 'finalized' + ) + + if (!transactions[0]?.signature) + throw new Error( + `could not find tx signature for proposal itx ${address.toBase58()}` + ) + + setTransactionSignature(transactions[0]?.signature) + } + + getSignature().catch((e) => console.warn(e.message)) + }, [connection, address]) + + return { transactionSignature } +} + +export default useTransactionSignature diff --git a/pages/dao/[symbol]/proposal/[pk].tsx b/pages/dao/[symbol]/proposal/[pk].tsx index 2a90a274ec..5da511d337 100644 --- a/pages/dao/[symbol]/proposal/[pk].tsx +++ b/pages/dao/[symbol]/proposal/[pk].tsx @@ -1,28 +1,28 @@ import Link from 'next/link' import ReactMarkdown from 'react-markdown/react-markdown.min' import { ArrowLeftIcon, ExternalLinkIcon } from '@heroicons/react/outline' -import useProposal from 'hooks/useProposal' -import ProposalStateBadge from 'components/ProposalStatusBadge' -import { InstructionPanel } from 'components/instructions/instructionPanel' + +import ProposalActionsPanel from '@components/ProposalActions' +import TokenBalanceCardWrapper from '@components/TokenBalance/TokenBalanceCardWrapper' + +import ApprovalQuorum from 'components/ApprovalQuorum' import DiscussionPanel from 'components/chat/DiscussionPanel' +import { InstructionPanel } from 'components/instructions/instructionPanel' +import ProposalStateBadge from 'components/ProposalStatusBadge' +import ProposalTimeStatus from 'components/ProposalTimeStatus' import VotePanel from 'components/VotePanel' -import ApprovalQuorum from 'components/ApprovalQuorum' -import useRealm from 'hooks/useRealm' -import useProposalVotes from 'hooks/useProposalVotes' import VoteResultsBar from 'components/VoteResultsBar' -import ProposalTimeStatus from 'components/ProposalTimeStatus' -import { option } from 'tools/core/option' +import useProposal from 'hooks/useProposal' +import useProposalVotes from 'hooks/useProposalVotes' import useQueryContext from 'hooks/useQueryContext' -import React from 'react' -import ProposalActionsPanel from '@components/ProposalActions' +import useRealm from 'hooks/useRealm' +import { option } from 'tools/core/option' import { getRealmExplorerHost } from 'tools/routing' -import TokenBalanceCardWrapper from '@components/TokenBalance/TokenBalanceCardWrapper' const Proposal = () => { const { fmtUrlWithCluster } = useQueryContext() const { symbol, realmInfo } = useRealm() const { proposal, description } = useProposal() - const { yesVoteProgress, yesVoteCount, From dcaf5623ad7c1f1e9825b5396b527037d1151fcd Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Sun, 20 Feb 2022 23:45:24 +0900 Subject: [PATCH 106/204] update raydium slippage from 0.5 to 1% --- tools/sdk/raydium/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/sdk/raydium/helpers.ts b/tools/sdk/raydium/helpers.ts index b1e01cc699..44111cbf2c 100644 --- a/tools/sdk/raydium/helpers.ts +++ b/tools/sdk/raydium/helpers.ts @@ -39,7 +39,7 @@ export const getAmountOut = async ( amountInBN ), currencyOut: new Token(poolKeys.quoteMint, quote.value.decimals), - slippage: new Percent(5, 1000), + slippage: new Percent(10, 1000), }) const currentPrice = amountOut.currentPrice.toFixed(quote.value.decimals) From 6457903038afde8b98bb4859b67d9e5f3f0df1fe Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Mon, 21 Feb 2022 01:17:17 +0900 Subject: [PATCH 107/204] update lock file --- yarn.lock | 58 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/yarn.lock b/yarn.lock index cbbbc90ef9..82bd3f649d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -568,6 +568,22 @@ version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" +"@blockworks-foundation/mango-client@3.2.15": + version "3.2.15" + resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.2.15.tgz#2b9fdb04068a420af6a8f60c42ca65a53e20a1bc" + integrity sha512-oyTMgQ7t6CjUIfJ26RygLg1eSu4zmMypD9iPWAglzFqJkmrwc0HOrr309ylEeUT1P5PxUxl3E3AaxjnhzLjMRw== + dependencies: + "@project-serum/anchor" "^0.16.2" + "@project-serum/serum" "0.13.55" + "@project-serum/sol-wallet-adapter" "^0.2.0" + "@solana/spl-token" "^0.1.6" + "@solana/web3.js" "1.21.0" + big.js "^6.1.1" + bn.js "^5.2.0" + buffer-layout "^1.2.1" + dotenv "^10.0.0" + yargs "^17.0.1" + "@blockworks-foundation/mango-client@^3.3.16": version "3.3.16" resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.3.16.tgz#e164cb90c3ae04a3822898b2e29bf280bde8d25a" @@ -1438,10 +1454,10 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/anchor@^0.18.0", "@project-serum/anchor@^0.18.2": - version "0.18.2" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.18.2.tgz#0f13b5c2046446b7c24cf28763eec90febb28485" - integrity sha512-uyjiN/3Ipp+4hrZRm/hG18HzGLZyvP790LXrCsGO3IWxSl28YRhiGEpKnZycfMW94R7nxdUoE3wY67V+ZHSQBQ== +"@project-serum/anchor@^0.16.2": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.16.2.tgz#b8b4ec4c749d59a224108f8d82ab68217ef752ae" + integrity sha512-wOJwObd4wOZ5tRRMCKYjeMNsEmf7vuC71KQRnw6wthhErL8c/818n4gYIZCf/1ZPl/8WPruIlmtQHDSEyy2+0Q== dependencies: "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" @@ -1458,19 +1474,18 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/anchor@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.21.0.tgz#ad5fb33744991ec1900cdb2fd22707c908b12b5f" - integrity sha512-flRuW/F+iC8mitNokx82LOXyND7Dyk6n5UUPJpQv/+NfySFrNFlzuQZaBZJ4CG5g9s8HS/uaaIz1nVkDR8V/QA== +"@project-serum/anchor@^0.18.0", "@project-serum/anchor@^0.18.2": + version "0.18.2" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.18.2.tgz#0f13b5c2046446b7c24cf28763eec90febb28485" + integrity sha512-uyjiN/3Ipp+4hrZRm/hG18HzGLZyvP790LXrCsGO3IWxSl28YRhiGEpKnZycfMW94R7nxdUoE3wY67V+ZHSQBQ== dependencies: - "@project-serum/borsh" "^0.2.4" + "@project-serum/borsh" "^0.2.2" "@solana/web3.js" "^1.17.0" base64-js "^1.5.1" bn.js "^5.1.2" bs58 "^4.0.1" - buffer-layout "^1.2.2" + buffer-layout "^1.2.0" camelcase "^5.3.1" - cross-fetch "^3.1.5" crypto-hash "^1.3.0" eventemitter3 "^4.0.7" find "^0.3.0" @@ -1499,6 +1514,27 @@ snake-case "^3.0.4" toml "^3.0.0" +"@project-serum/anchor@^0.21.0": + version "0.21.0" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.21.0.tgz#ad5fb33744991ec1900cdb2fd22707c908b12b5f" + integrity sha512-flRuW/F+iC8mitNokx82LOXyND7Dyk6n5UUPJpQv/+NfySFrNFlzuQZaBZJ4CG5g9s8HS/uaaIz1nVkDR8V/QA== + dependencies: + "@project-serum/borsh" "^0.2.4" + "@solana/web3.js" "^1.17.0" + base64-js "^1.5.1" + bn.js "^5.1.2" + bs58 "^4.0.1" + buffer-layout "^1.2.2" + camelcase "^5.3.1" + cross-fetch "^3.1.5" + crypto-hash "^1.3.0" + eventemitter3 "^4.0.7" + find "^0.3.0" + js-sha256 "^0.9.0" + pako "^2.0.3" + snake-case "^3.0.4" + toml "^3.0.0" + "@project-serum/borsh@^0.2.2": version "0.2.2" resolved "https://registry.npmjs.org/@project-serum/borsh/-/borsh-0.2.2.tgz" From 16279b2085097ffe15a31a652bbfcb616037defc Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Wed, 23 Feb 2022 22:40:02 +0900 Subject: [PATCH 108/204] modif from previous merge --- package.json | 2 +- .../solend/CreateObligationAccount.tsx | 55 +++++++++--------- ...eserveLiquidityAndObligationCollateral.tsx | 39 ++++++------- .../solend/InitObligationAccount.tsx | 52 +++++++++-------- .../instructions/solend/RefreshObligation.tsx | 31 +++++----- .../instructions/solend/RefreshReserve.tsx | 58 +++++++++++++++---- ...ionCollateralAndRedeemReserveLiquidity.tsx | 52 +++++++++++------ 7 files changed, 172 insertions(+), 117 deletions(-) diff --git a/package.json b/package.json index 1f5718bc28..d0406a0f9c 100644 --- a/package.json +++ b/package.json @@ -100,4 +100,4 @@ "preset": "emotion" } } -} \ No newline at end of file +} diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx index fce5942933..5f8ce2ed40 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx @@ -1,23 +1,25 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' +import { + Governance, + ProgramAccount, + serializeInstructionToBase64, +} from '@solana/spl-governance' import * as yup from 'yup' +import { PublicKey } from '@solana/web3.js' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' +import useRealm from '@hooks/useRealm' +import { createObligationAccount } from '@tools/sdk/solend/createObligationAccount' import { isFormValid } from '@utils/formValidation' import { - UiInstruction, CreateSolendObligationAccountForm, + UiInstruction, } from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' + +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import useWalletStore from 'stores/useWalletStore' -import { - ProgramAccount, - serializeInstructionToBase64, - Governance, -} from '@solana/spl-governance' + +import { NewProposalContext } from '../../../new' import GovernedAccountSelect from '../../GovernedAccountSelect' -import { createObligationAccount } from '@tools/sdk/solend/createObligationAccount' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const CreateObligationAccount = ({ index, @@ -29,13 +31,12 @@ const CreateObligationAccount = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} } - + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({}) @@ -87,7 +88,7 @@ const CreateObligationAccount = ({ propertyName: 'programId', value: programId?.toString(), }) - }, [realmInfo?.programId]) + }, [programId]) useEffect(() => { handleSetInstructions( @@ -107,19 +108,17 @@ const CreateObligationAccount = ({ }) return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx index cf8ef1d293..ac6be4ce2d 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx @@ -1,30 +1,29 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' +import BigNumber from 'bignumber.js' import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - DepositReserveLiquidityAndObligationCollateralForm, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' -import useWalletStore from 'stores/useWalletStore' + +import { BN } from '@project-serum/anchor' import { + Governance, ProgramAccount, serializeInstructionToBase64, - Governance, } from '@solana/spl-governance' -import GovernedAccountSelect from '../../GovernedAccountSelect' +import { PublicKey } from '@solana/web3.js' import Input from '@components/inputs/Input' -import { depositReserveLiquidityAndObligationCollateral } from '@tools/sdk/solend/depositReserveLiquidityAndObligationCollateral' import Select from '@components/inputs/Select' - -import SolendConfiguration from '@tools/sdk/solend/configuration' - -import BigNumber from 'bignumber.js' -import { BN } from '@project-serum/anchor' import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' +import useRealm from '@hooks/useRealm' +import SolendConfiguration from '@tools/sdk/solend/configuration' +import { depositReserveLiquidityAndObligationCollateral } from '@tools/sdk/solend/depositReserveLiquidityAndObligationCollateral' +import { isFormValid } from '@utils/formValidation' +import { + DepositReserveLiquidityAndObligationCollateralForm, + UiInstruction, +} from '@utils/uiTypes/proposalCreationTypes' +import useWalletStore from 'stores/useWalletStore' +import { NewProposalContext } from '../../../new' +import GovernedAccountSelect from '../../GovernedAccountSelect' const DepositReserveLiquidityAndObligationCollateral = ({ index, @@ -36,8 +35,8 @@ const DepositReserveLiquidityAndObligationCollateral = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} @@ -108,7 +107,7 @@ const DepositReserveLiquidityAndObligationCollateral = ({ propertyName: 'programId', value: programId?.toString(), }) - }, [realmInfo?.programId]) + }, [programId]) useEffect(() => { handleSetInstructions( @@ -144,7 +143,7 @@ const DepositReserveLiquidityAndObligationCollateral = ({ error={formErrors['governedAccount']} shouldBeGoverned={shouldBeGoverned} governance={governance} - > + /> { +import { NewProposalContext } from '../../../new' +import GovernedAccountSelect from '../../GovernedAccountSelect' + +const RefreshReserve = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({}) const [formErrors, setFormErrors] = useState({}) @@ -49,12 +66,13 @@ const RefreshReserve = ({ index }: { index: number }) => { !isValid || !programId || !form.mintName || + !form.governedAccount?.governance.account || !wallet?.publicKey ) { return { serializedInstruction: '', isValid: false, - governance: undefined, + governance: form.governedAccount?.governance, } } @@ -65,7 +83,7 @@ const RefreshReserve = ({ index }: { index: number }) => { return { serializedInstruction: serializeInstructionToBase64(tx), isValid: true, - governance: undefined, + governance: form.governedAccount?.governance, } } @@ -74,11 +92,12 @@ const RefreshReserve = ({ index }: { index: number }) => { propertyName: 'programId', value: programId?.toString(), }) - }, [realmInfo?.programId]) + }, [programId]) useEffect(() => { handleSetInstructions( { + governedAccount: form.governedAccount?.governance, getInstruction, }, index @@ -86,11 +105,26 @@ const RefreshReserve = ({ index }: { index: number }) => { }, [form]) const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), mintName: yup.string().required('Token Name is required'), }) return ( <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + /> + + + handleSetForm({ + value: evt.target.value, + propertyName: 'destinationLiquidity', + }) + } + error={formErrors['destinationLiquidity']} + /> ) } From 030af3908acab5b3c1ee21b044a560e3e87e0499 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Wed, 23 Feb 2022 22:45:10 +0900 Subject: [PATCH 109/204] remove anchor patch --- package.json | 3 +-- patches/@project-serum+anchor+0.19.0.patch | 22 ----------------- yarn.lock | 28 ++++------------------ 3 files changed, 5 insertions(+), 48 deletions(-) delete mode 100644 patches/@project-serum+anchor+0.19.0.patch diff --git a/package.json b/package.json index d0406a0f9c..ad9cb39510 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "lint": "eslint . --ext ts --ext tsx --ext js --ext jsx", "test": "jest", "test-all": "yarn lint && yarn type-check && yarn test", - "postinstall": "patch-package", "notifier": "ts-node scripts/governance-notifier.ts" }, "lint-staged": { @@ -100,4 +99,4 @@ "preset": "emotion" } } -} +} \ No newline at end of file diff --git a/patches/@project-serum+anchor+0.19.0.patch b/patches/@project-serum+anchor+0.19.0.patch deleted file mode 100644 index 45aa302cd1..0000000000 --- a/patches/@project-serum+anchor+0.19.0.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/node_modules/@project-serum/anchor/dist/esm/program/common.js b/node_modules/@project-serum/anchor/dist/esm/program/common.js -index 164d8ff..bab8614 100644 ---- a/node_modules/@project-serum/anchor/dist/esm/program/common.js -+++ b/node_modules/@project-serum/anchor/dist/esm/program/common.js -@@ -38,15 +38,6 @@ export function validateAccounts(ixAccounts, accounts = {}) { - } - // Translates an address to a Pubkey. - export function translateAddress(address) { -- if (typeof address === "string") { -- const pk = new PublicKey(address); -- return pk; -- } -- else if (address.constructor.prototype.constructor.name === "PublicKey") { -- return address; -- } -- else { -- throw new Error("Given address is not a PublicKey nor a string."); -- } -+ return new PublicKey(address); - } - //# sourceMappingURL=common.js.map -\ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 0d7c291a96..31496f47ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1442,26 +1442,6 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/anchor@^0.19.0": - version "0.19.0" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.19.0.tgz#79f1fbe7c3134860ccbfe458a0e09daf79644885" - integrity sha512-cs0LBmJOrL9eJ8MRNqitnzbpCT5QEzVdJmiIjfNV5YaGn1K9vISR7DtISj3Bdl3KBdLqii4CTw1mpHdi8iXUCg== - dependencies: - "@project-serum/borsh" "^0.2.2" - "@solana/web3.js" "^1.17.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^5.3.1" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - find "^0.3.0" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - "@project-serum/anchor@^0.21.0": version "0.21.0" resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.21.0.tgz#ad5fb33744991ec1900cdb2fd22707c908b12b5f" @@ -1981,10 +1961,10 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solendprotocol/solend-sdk@^0.4.7": - version "0.4.7" - resolved "https://registry.yarnpkg.com/@solendprotocol/solend-sdk/-/solend-sdk-0.4.7.tgz#fd840994f71a4d16be1b1d35bbe8e446d150a7e5" - integrity sha512-jOoZr+B9QFbHNnKZ5Au05lj0+fEu81Eq+5dAxrTyDtG1TOiZgtAlyMO1jV3Uykj5FQrc9V8NA36E26lkHUXthA== +"@solendprotocol/solend-sdk@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@solendprotocol/solend-sdk/-/solend-sdk-0.4.9.tgz#c0e3f24148c4951bf0cfdf16c884bc4ffbb42b7b" + integrity sha512-tVsvE0kap58/eMeeSonv0tEiqlnvYC0x90MXKoGz2D0oyQBx9Eneba5wXDh9FJ3S5S2RFqwXIAnmdOzTLZzV/A== dependencies: "@pythnetwork/client" "^2.5.1" "@solana/buffer-layout" "^3.0.0" From 406b2fe518c453050f847c738e56336f5e68e377 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Wed, 23 Feb 2022 22:48:18 +0900 Subject: [PATCH 110/204] merge from source repo --- Strategies/components/DepositComponent.tsx | 1 + .../components/MangoDepositComponent.tsx | 19 +- Strategies/protocols/mango/tools.ts | 3 +- Strategies/types/types.ts | 3 +- .../components/Account/DepositCard.tsx | 4 +- .../components/instructions/Grant.tsx | 16 +- actions/executeInstructions.ts | 19 +- components/ApprovalQuorum.tsx | 4 +- components/ConnectWalletButton.tsx | 31 +++- components/Modal.tsx | 2 +- components/NFTS/NFTSelector.tsx | 2 +- components/ProposalCard.tsx | 8 +- .../components/Steps/WizardModeSelect.tsx | 11 ++ components/VoteCountdown.tsx | 4 +- components/VoteResultStatus.tsx | 6 +- components/chat/Comment.tsx | 13 +- .../ExecuteAllInstructionButton.tsx | 42 ++--- components/instructions/instructionCard.tsx | 6 +- components/instructions/instructionPanel.tsx | 26 ++- .../programs/associatedTokenAccount.tsx | 31 ++-- components/instructions/programs/names.ts | 21 +-- components/instructions/programs/solend.tsx | 53 +++--- components/instructions/programs/system.tsx | 10 +- .../programs/voteStakeRegistry.tsx | 2 +- components/instructions/tools.tsx | 13 +- hooks/handleGovernanceAssetsStore.ts | 95 ++++++++++ hooks/useGovernanceAssets.ts | 158 ++++++----------- hooks/useGovernedMultiTypeAccounts.ts | 8 +- hooks/useIsBeyondTimestamp.ts | 6 +- hooks/useProposalVotes.tsx | 4 + package.json | 12 +- pages/_app.tsx | 2 + pages/dao/[symbol]/index.tsx | 47 ++++- pages/dao/[symbol]/proposal/[pk].tsx | 4 +- .../proposal/components/ApproveAllBtn.tsx | 3 - .../CreateAssociatedTokenAccount.tsx | 54 +++--- .../components/instructions/CustomBase64.tsx | 19 +- .../components/instructions/Empty.tsx | 34 ++-- .../solend/CreateObligationAccount.tsx | 55 +++--- ...eserveLiquidityAndObligationCollateral.tsx | 39 ++--- .../solend/InitObligationAccount.tsx | 52 +++--- .../instructions/solend/RefreshObligation.tsx | 31 ++-- .../instructions/solend/RefreshReserve.tsx | 58 +++++-- ...ionCollateralAndRedeemReserveLiquidity.tsx | 52 ++++-- pages/dao/[symbol]/proposal/new.tsx | 62 +------ patches/@project-serum+anchor+0.19.0.patch | 22 --- public/realms/DAKU/img/daku_logo.png | Bin 0 -> 120992 bytes public/realms/UXP/img/UXP-Black.png | Bin 0 -> 7724 bytes public/realms/mainnet-beta.json | 24 +-- scripts/api.ts | 8 +- scripts/governance-notifier.ts | 3 +- stores/useGovernanceAssetsStore.tsx | 37 ++++ tools/sdk/solend/configuration.ts | 42 ++--- ...ReserveLiquidityAndObligationCollateral.ts | 2 +- ...tionCollateralAndRedeemReserveLiquidity.ts | 7 +- utils/associated.tsx | 2 +- utils/ataTools.tsx | 16 ++ utils/formatting.tsx | 9 +- utils/splTokens.ts | 3 +- utils/uiTypes/proposalCreationTypes.ts | 8 + yarn.lock | 162 +++++------------- 61 files changed, 776 insertions(+), 714 deletions(-) create mode 100644 hooks/handleGovernanceAssetsStore.ts delete mode 100644 patches/@project-serum+anchor+0.19.0.patch create mode 100644 public/realms/DAKU/img/daku_logo.png create mode 100644 public/realms/UXP/img/UXP-Black.png create mode 100644 stores/useGovernanceAssetsStore.tsx diff --git a/Strategies/components/DepositComponent.tsx b/Strategies/components/DepositComponent.tsx index bee01a8410..f36658e17f 100644 --- a/Strategies/components/DepositComponent.tsx +++ b/Strategies/components/DepositComponent.tsx @@ -120,6 +120,7 @@ const DepositComponent = ({ ownTokenRecord.pubkey, defaultProposalMint!, matchedTreasuryAccount!.governance!.account!.proposalCount, + [], false, market, client diff --git a/Strategies/components/MangoDepositComponent.tsx b/Strategies/components/MangoDepositComponent.tsx index 3118f4d7d6..e25e58c30b 100644 --- a/Strategies/components/MangoDepositComponent.tsx +++ b/Strategies/components/MangoDepositComponent.tsx @@ -23,7 +23,6 @@ import { withCreateNativeTreasury, } from '@solana/spl-governance' import { - Keypair, SystemProgram, Transaction, TransactionInstruction, @@ -149,7 +148,6 @@ const MangoDepositComponent = ({ }, [matchedTreasuryAccount]) const handleSolPayment = async () => { const instructions: TransactionInstruction[] = [] - const signers: Keypair[] = [] const toAddress = await getNativeTreasuryAddress( realm!.owner, matchedTreasuryAccount!.governance!.pubkey @@ -176,17 +174,7 @@ const MangoDepositComponent = ({ lamports: minRentAmount, }) instructions.push(transferIx) - const transaction = new Transaction() - transaction.add(...instructions) - - await sendTransaction({ - transaction, - wallet, - connection: connection.current, - signers, - sendingMessage: 'Depositing sol to create mango account', - successMessage: 'Sol deposited', - }) + return instructions } const changeMode = (val) => { setIsDepositMode(!val) @@ -194,6 +182,7 @@ const MangoDepositComponent = ({ const handleDeposit = async () => { try { setIsDepositing(true) + const prerequisiteInstructions: TransactionInstruction[] = [] const group = market.group! const groupConfig = market.groupConfig! const [mangoAccountPk] = await PublicKey.findProgramAddress( @@ -209,7 +198,8 @@ const MangoDepositComponent = ({ 'processed' ) if (!acc) { - await handleSolPayment() + const solAccountInstruction = await handleSolPayment() + prerequisiteInstructions.push(...solAccountInstruction) } const rpcContext = new RpcContext( new PublicKey(realm!.owner.toString()), @@ -239,6 +229,7 @@ const MangoDepositComponent = ({ ownTokenRecord.pubkey, defaultProposalMint!, matchedTreasuryAccount!.governance!.account!.proposalCount, + prerequisiteInstructions, false, market, client diff --git a/Strategies/protocols/mango/tools.ts b/Strategies/protocols/mango/tools.ts index a58b916e5e..f20a30de9c 100644 --- a/Strategies/protocols/mango/tools.ts +++ b/Strategies/protocols/mango/tools.ts @@ -117,6 +117,7 @@ const HandleMangoDeposit: HandleCreateProposalWithStrategy = async ( tokenOwnerRecord, governingTokenMint, proposalIndex, + prerequisiteInstructions, isDraft, market, client @@ -169,7 +170,7 @@ const HandleMangoDeposit: HandleCreateProposalWithStrategy = async ( ), holdUpTime: matchedTreasury.governance!.account!.config .minInstructionHoldUpTime, - prerequisiteInstructions: [], + prerequisiteInstructions: [...prerequisiteInstructions], splitToChunkByDefault: true, } const instructionData2 = { diff --git a/Strategies/types/types.ts b/Strategies/types/types.ts index 4e13ed0331..bc89e544df 100644 --- a/Strategies/types/types.ts +++ b/Strategies/types/types.ts @@ -1,7 +1,7 @@ import { VsrClient } from '@blockworks-foundation/voter-stake-registry-client' import { BN } from '@project-serum/anchor' import { ProgramAccount, Realm, RpcContext } from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' +import { PublicKey, TransactionInstruction } from '@solana/web3.js' import { GovernedTokenAccount } from '@utils/tokens' import { MarketStore } from 'Strategies/store/marketStore' @@ -35,6 +35,7 @@ export type HandleCreateProposalWithStrategy = ( tokenOwnerRecord: PublicKey, governingTokenMint: PublicKey, proposalIndex: number, + prerequisiteInstructions: TransactionInstruction[], isDraft: boolean, market?: MarketStore, client?: VsrClient diff --git a/VoteStakeRegistry/components/Account/DepositCard.tsx b/VoteStakeRegistry/components/Account/DepositCard.tsx index f7e8ae3b05..682dbb3d10 100644 --- a/VoteStakeRegistry/components/Account/DepositCard.tsx +++ b/VoteStakeRegistry/components/Account/DepositCard.tsx @@ -28,7 +28,7 @@ import { abbreviateAddress } from '@utils/formatting' import { notify } from '@utils/notifications' import useVoteStakeRegistryClientStore from 'VoteStakeRegistry/stores/voteStakeRegistryClientStore' import { calcMintMultiplier } from 'VoteStakeRegistry/tools/deposits' -import moment from 'moment' +import dayjs from 'dayjs' const DepositCard = ({ deposit }: { deposit: DepositWithMintAccount }) => { const { getOwnedDeposits } = useDepositStore() @@ -198,7 +198,7 @@ const DepositCard = ({ deposit }: { deposit: DepositWithMintAccount }) => { {isVest && deposit.nextVestingTimestamp !== null && ( diff --git a/VoteStakeRegistry/components/instructions/Grant.tsx b/VoteStakeRegistry/components/instructions/Grant.tsx index 311a529538..c6f840b053 100644 --- a/VoteStakeRegistry/components/instructions/Grant.tsx +++ b/VoteStakeRegistry/components/instructions/Grant.tsx @@ -32,13 +32,13 @@ import GovernedAccountSelect from 'pages/dao/[symbol]/proposal/components/Govern import { lockupTypes } from 'VoteStakeRegistry/tools/types' import Select from '@components/inputs/Select' import Switch from '@components/Switch' -import moment from 'moment' import { getFormattedStringFromDays } from 'VoteStakeRegistry/tools/dateTools' import * as yup from 'yup' import { getGrantInstruction } from 'VoteStakeRegistry/actions/getGrantInstruction' import { getRegistrarPDA } from 'VoteStakeRegistry/sdk/accounts' import { tryGetRegistrar } from 'VoteStakeRegistry/sdk/api' import useVoteStakeRegistryClientStore from 'VoteStakeRegistry/stores/voteStakeRegistryClientStore' +import dayjs from 'dayjs' const Grant = ({ index, @@ -48,13 +48,13 @@ const Grant = ({ governance: ProgramAccount | null }) => { const client = useVoteStakeRegistryClientStore((s) => s.state.client) - const dateNow = moment().unix() + const dateNow = dayjs().unix() const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realm, tokenRecords } = useRealm() const { governedTokenAccountsWithoutNfts } = useGovernanceAssets() const shouldBeGoverned = index !== 0 && governance - const [startDate, setStartDate] = useState(moment().format('DD-MM-YYYY')) + const [startDate, setStartDate] = useState(dayjs().format('DD-MM-YYYY')) const [endDate, setEndDate] = useState('') const [useableGrantMints, setUseableGrantMints] = useState([]) const [form, setForm] = useState({ @@ -165,7 +165,7 @@ const Grant = ({ const handleChangeStartDate = (e) => { const value = e.target.value setStartDate(value) - const unixDate = moment(value).unix() + const unixDate = dayjs(value).unix() handleSetForm({ value: !isNaN(unixDate) ? unixDate : 0, propertyName: 'startDateUnixSeconds', @@ -179,11 +179,11 @@ const Grant = ({ if ( startDate && endDate && - moment(startDate).isValid() && - moment(endDate).isValid() + dayjs(startDate).isValid() && + dayjs(endDate).isValid() ) { - const daysDifference = moment(endDate).diff(moment(startDate), 'days') - const monthsDifference = moment(endDate).diff(moment(startDate), 'months') + const daysDifference = dayjs(endDate).diff(dayjs(startDate), 'days') + const monthsDifference = dayjs(endDate).diff(dayjs(startDate), 'months') const periods = form.lockupKind.value !== 'monthly' ? daysDifference : monthsDifference diff --git a/actions/executeInstructions.ts b/actions/executeInstructions.ts index a946d5c3dc..f2cb59ba4e 100644 --- a/actions/executeInstructions.ts +++ b/actions/executeInstructions.ts @@ -1,14 +1,12 @@ -import { Proposal, ProposalTransaction } from '@solana/spl-governance' - -import { withExecuteTransaction } from '@solana/spl-governance' -import { RpcContext } from '@solana/spl-governance' -import { ProgramAccount } from '@solana/spl-governance' -import { sendSignedTransaction, signTransaction } from '@utils/send' - +import { + ProgramAccount, + Proposal, + ProposalTransaction, + RpcContext, + withExecuteTransaction, +} from '@solana/spl-governance' import { Transaction, TransactionInstruction } from '@solana/web3.js' - -// Magic number (?) -const DEFAULT_TIMEOUT = 31_000 +import { sendSignedTransaction, signTransaction } from '@utils/send' // Merge instructions within one Transaction, sign it and execute it export const executeInstructions = async ( @@ -49,6 +47,5 @@ export const executeInstructions = async ( connection, sendingMessage: 'Executing instruction', successMessage: 'Execution finalized', - timeout: DEFAULT_TIMEOUT, }) } diff --git a/components/ApprovalQuorum.tsx b/components/ApprovalQuorum.tsx index 7dc147e7cb..fe788a3000 100644 --- a/components/ApprovalQuorum.tsx +++ b/components/ApprovalQuorum.tsx @@ -22,7 +22,7 @@ const ApprovalProgress = ({ ? showBg ? 'bg-bkg-1 p-3' : '' - : 'border border-green p-3' + : 'border border-fgd-4 flex h-full p-3' } rounded-md`} >
@@ -46,7 +46,7 @@ const ApprovalProgress = ({ { maximumFractionDigits: 0, } - )} 'Yes' votes required`}

+ )} more Yes vote${yesVotesRequired > 1 ? 's' : ''} required`}

{progress.toFixed(1)}%

diff --git a/components/ConnectWalletButton.tsx b/components/ConnectWalletButton.tsx index 5ca86bc5d1..082a448b07 100644 --- a/components/ConnectWalletButton.tsx +++ b/components/ConnectWalletButton.tsx @@ -1,5 +1,5 @@ import { Menu } from '@headlessui/react' -import { useMemo } from 'react' +import { useMemo, useState } from 'react' import { CheckCircleIcon, ChevronDownIcon } from '@heroicons/react/solid' import styled from '@emotion/styled' import useWalletStore from '../stores/useWalletStore' @@ -16,7 +16,9 @@ import { import { BackspaceIcon } from '@heroicons/react/solid' import { UserCircleIcon } from '@heroicons/react/outline' import { abbreviateAddress } from '@utils/formatting' +import { useRouter } from 'next/router' import TwitterIcon from './TwitterIcon' +import Switch from './Switch' const StyledWalletProviderLabel = styled.p` font-size: 0.65rem; @@ -36,6 +38,17 @@ const ConnectWalletButton = (props) => { providerUrl, ]) + const [useDevnet, setUseDevnet] = useState(false) + const router = useRouter() + const handleToggleDevnet = () => { + setUseDevnet(!useDevnet) + if (useDevnet) { + router.push(`${window.location.pathname}`) + } else { + router.push(`${window.location.href}?cluster=devnet`) + } + } + const handleConnectDisconnect = async () => { try { if (connected) { @@ -183,6 +196,22 @@ const ConnectWalletButton = (props) => { Disconnect + { + handleToggleDevnet() + }} + > + + )} diff --git a/components/Modal.tsx b/components/Modal.tsx index 3ff040b75c..a48e1004be 100644 --- a/components/Modal.tsx +++ b/components/Modal.tsx @@ -1,5 +1,5 @@ -import { Portal } from 'react-portal' import { XIcon } from '@heroicons/react/outline' +import { Portal } from 'react-portal' const Modal = ({ isOpen, diff --git a/components/NFTS/NFTSelector.tsx b/components/NFTS/NFTSelector.tsx index 458430a5a3..5bda26c30e 100644 --- a/components/NFTS/NFTSelector.tsx +++ b/components/NFTS/NFTSelector.tsx @@ -83,7 +83,7 @@ function NFTSelector( {selectedNfts.find( (selectedNfts) => selectedNfts.mint === x.mint ) && ( - + )}
diff --git a/components/ProposalCard.tsx b/components/ProposalCard.tsx index 6690e63036..a6a1c3c761 100644 --- a/components/ProposalCard.tsx +++ b/components/ProposalCard.tsx @@ -30,9 +30,7 @@ const StyledCardWrapper = styled.div` const ProposalCard = ({ proposalPk, proposal }: ProposalCardProps) => { const { symbol } = useRealm() const { fmtUrlWithCluster } = useQueryContext() - const { yesVoteProgress, minimumYesVotes, yesVoteCount } = useProposalVotes( - proposal - ) + const { yesVoteProgress, yesVotesRequired } = useProposalVotes(proposal) return (
@@ -59,13 +57,13 @@ const ProposalCard = ({ proposalPk, proposal }: ProposalCardProps) => {
{proposal.state === ProposalState.Voting && (
-
+
diff --git a/components/RealmWizard/components/Steps/WizardModeSelect.tsx b/components/RealmWizard/components/Steps/WizardModeSelect.tsx index 82abcf90d4..8c0b61f240 100644 --- a/components/RealmWizard/components/Steps/WizardModeSelect.tsx +++ b/components/RealmWizard/components/Steps/WizardModeSelect.tsx @@ -45,6 +45,17 @@ const WizardModeSelect: React.FC<{
+ ) } diff --git a/components/VoteCountdown.tsx b/components/VoteCountdown.tsx index 3118ffeaa9..f0b32a7fa1 100644 --- a/components/VoteCountdown.tsx +++ b/components/VoteCountdown.tsx @@ -1,6 +1,6 @@ -import moment from 'moment' import React, { useEffect, useState } from 'react' import { Governance, Proposal } from '@solana/spl-governance' +import dayjs from 'dayjs' interface CountdownState { days: number @@ -38,7 +38,7 @@ export function VoteCountdown({ } const getTimeToVoteEnd = () => { - const now = moment().unix() + const now = dayjs().unix() let timeToVoteEnd = proposal.isPreVotingState() ? governance.config.maxVotingTime diff --git a/components/VoteResultStatus.tsx b/components/VoteResultStatus.tsx index 6a01e8324c..5219f46551 100644 --- a/components/VoteResultStatus.tsx +++ b/components/VoteResultStatus.tsx @@ -6,11 +6,7 @@ type VoteResultStatusProps = { const VoteResultStatus = ({ votePassed }: VoteResultStatusProps) => { return ( -
+
{votePassed ? ( ) : ( diff --git a/components/chat/Comment.tsx b/components/chat/Comment.tsx index 63caedf8b1..977dfe9003 100644 --- a/components/chat/Comment.tsx +++ b/components/chat/Comment.tsx @@ -1,4 +1,3 @@ -import moment from 'moment' import React from 'react' import { VoteRecord } from '@solana/spl-governance' import { @@ -13,7 +12,8 @@ import useRealm from '../../hooks/useRealm' import { MintInfo } from '@solana/spl-token' import { isPublicKey } from '@tools/core/pubkey' import { getVoteWeight, isYesVote } from '@models/voteRecords' - +import dayjs from 'dayjs' +const relativeTime = require('dayjs/plugin/relativeTime') const Comment = ({ chatMessage, voteRecord, @@ -23,15 +23,16 @@ const Comment = ({ voteRecord: VoteRecord | undefined proposalMint: MintInfo | undefined }) => { + dayjs.extend(relativeTime) const { author, postedAt, body } = chatMessage const { realmInfo } = useRealm() - const voteSymbol = !realmInfo ? '' : isPublicKey(realmInfo.symbol) ? realmInfo.displayName : realmInfo.symbol - + //@ts-ignore + const fromNow = dayjs(postedAt.toNumber() * 1000).fromNow() return (
@@ -53,9 +54,7 @@ const Comment = ({ className={`flex-shrink-0 h-4 w-4 ml-1.5 text-primary-light`} /> -
- {moment.unix(postedAt.toNumber()).fromNow()} -
+
{fromNow}
{voteRecord && ( diff --git a/components/instructions/ExecuteAllInstructionButton.tsx b/components/instructions/ExecuteAllInstructionButton.tsx index aeef6f9fc3..fd6879b5de 100644 --- a/components/instructions/ExecuteAllInstructionButton.tsx +++ b/components/instructions/ExecuteAllInstructionButton.tsx @@ -1,21 +1,20 @@ -import { useEffect, useState } from 'react' +import React, { useEffect, useState } from 'react' import { InstructionExecutionStatus, + ProgramAccount, Proposal, - ProposalTransaction, ProposalState, + ProposalTransaction, + RpcContext, } from '@solana/spl-governance' -import React from 'react' +import { PublicKey } from '@solana/web3.js' import { CheckCircleIcon, PlayIcon, RefreshIcon } from '@heroicons/react/solid' import Button from '@components/Button' -import { RpcContext } from '@solana/spl-governance' -import useRealm from '@hooks/useRealm' -import useWalletStore from 'stores/useWalletStore' -import { ProgramAccount } from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' import Tooltip from '@components/Tooltip' +import useRealm from '@hooks/useRealm' import { getProgramVersionForRealm } from '@models/registry/api' import { executeInstructions } from 'actions/executeInstructions' +import useWalletStore from 'stores/useWalletStore' export enum PlayState { Played, @@ -47,7 +46,7 @@ export function ExecuteAllInstructionButton({ ? proposal.account.votingCompletedAt.toNumber() + 1 : 0 - const ineligibleToSee = currentSlot - canExecuteAt >= 0 + const isPassedExecutionSlot = currentSlot - canExecuteAt >= 0 const rpcContext = new RpcContext( new PublicKey(proposal.owner.toString()), @@ -56,9 +55,10 @@ export function ExecuteAllInstructionButton({ connection.current, connection.endpoint ) - + // update the current slot every 5 seconds + // if current slot > slot available to execute the transaction useEffect(() => { - if (ineligibleToSee && proposal) { + if (isPassedExecutionSlot && proposal) { const timer = setTimeout(() => { rpcContext.connection.getSlot().then(setCurrentSlot) }, 5000) @@ -67,7 +67,7 @@ export function ExecuteAllInstructionButton({ clearTimeout(timer) } } - }, [ineligibleToSee, rpcContext.connection, currentSlot]) + }, [isPassedExecutionSlot, rpcContext.connection, currentSlot]) const onExecuteInstructions = async () => { setPlaying(PlayState.Playing) @@ -76,7 +76,7 @@ export function ExecuteAllInstructionButton({ await executeInstructions(rpcContext, proposal, proposalInstructions) await fetchRealm(realmInfo?.programId, realmInfo?.realmId) } catch (error) { - console.log('error executing instruction', error) + console.error('error executing instruction', error) setPlaying(PlayState.Error) @@ -99,21 +99,23 @@ export function ExecuteAllInstructionButton({ } if ( - proposal.account.state !== ProposalState.Executing && - proposal.account.state !== ProposalState.ExecutingWithErrors && - proposal.account.state !== ProposalState.Succeeded + ![ + ProposalState.Executing, + ProposalState.ExecutingWithErrors, + ProposalState.Succeeded, + ].includes(proposal.account.state) ) { return null } - if (ineligibleToSee) { + if (isPassedExecutionSlot) { return null } if ( playing === PlayState.Unplayed && proposalInstructions.every( - (x) => x.account.executionStatus !== InstructionExecutionStatus.Error + (itx) => itx.account.executionStatus !== InstructionExecutionStatus.Error ) ) { return ( @@ -130,7 +132,7 @@ export function ExecuteAllInstructionButton({ if ( playing === PlayState.Error || proposalInstructions.every( - (x) => x.account.executionStatus !== InstructionExecutionStatus.Error + (itx) => itx.account.executionStatus !== InstructionExecutionStatus.Error ) ) { return ( @@ -143,5 +145,5 @@ export function ExecuteAllInstructionButton({ ) } - return + return } diff --git a/components/instructions/instructionCard.tsx b/components/instructions/instructionCard.tsx index 4595ac30a0..a4681c935d 100644 --- a/components/instructions/instructionCard.tsx +++ b/components/instructions/instructionCard.tsx @@ -106,11 +106,7 @@ export default function InstructionCard({ return } getAmountImg() - }, [ - proposalInstruction, - governedTokenAccountsWithoutNfts.length, - governedTokenAccountsWithoutNfts.length, - ]) + }, [proposalInstruction, governedTokenAccountsWithoutNfts.length]) const isSol = tokenImgUrl.includes(WSOL_MINT) const proposalAuthority = tokenRecords[proposal.owner.toBase58()] diff --git a/components/instructions/instructionPanel.tsx b/components/instructions/instructionPanel.tsx index 0b8ca7d5be..a62d66ba66 100644 --- a/components/instructions/instructionPanel.tsx +++ b/components/instructions/instructionPanel.tsx @@ -92,21 +92,19 @@ export function InstructionPanel() { - {proposalInstructions.map((pi, idx) => { - return ( -
- {proposal && ( - - )} -
- ) - })} + {proposalInstructions.map((pi, idx) => ( +
+ {proposal && ( + + )} +
+ ))} - {proposal && ( + {proposal && proposalInstructions.length > 1 && (
{ const ata = accounts[1].pubkey.toString() const tokenMint = accounts[3].pubkey.toString() - - const tokenName = Object.keys(SPL_TOKENS).reduce((tmp, x) => { - const { mint, name } = SPL_TOKENS[x] - - if (mint.toString() === tokenMint) { - return name - } - - return tmp - }, 'unknown') + const tokenName = + SPL_TOKENS[ + Object.keys(SPL_TOKENS).find( + (name) => SPL_TOKENS[name].mint?.toString() === tokenMint + )! + ].name ?? 'unknown' return ( -
-
+
+
New Associated Token Amount Address: {ata}
-
+
Token Name {tokenName}
diff --git a/components/instructions/programs/names.ts b/components/instructions/programs/names.ts index 4ede88d062..a0aa00db7a 100644 --- a/components/instructions/programs/names.ts +++ b/components/instructions/programs/names.ts @@ -1,22 +1,22 @@ import { PublicKey } from '@solana/web3.js' export const GOVERNANCE_PROGRAM_NAMES = { - GqTPL6qRf5aUuqscLh8Rg2HTxPUXfhhAXDptTLhp1t2J: 'Mango Governance', - AVoAYTs36yB5izAaBkxRG67wL1AMwG3vo41hKtUSb8is: 'Serum Governance', - GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw: 'Governance', - '5sGZEdn32y8nHax7TxEyoHuPS3UXfPWtisgm8kqxat8H': 'Phantasia Governance', + GqTPL6qRf5aUuqscLh8Rg2HTxPUXfhhAXDptTLhp1t2J: 'Mango Governance Program', + AVoAYTs36yB5izAaBkxRG67wL1AMwG3vo41hKtUSb8is: 'Serum Governance Program', + GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw: 'Governance Program', + '5sGZEdn32y8nHax7TxEyoHuPS3UXfPWtisgm8kqxat8H': + 'Phantasia Governance Program', smfjietFKFJ4Sbw1cqESBTpPhF4CwbMwN8kBEC1e5ui: - 'Strangemood Foundation Governance', - UXGA4U6nmCkdpVnhrhRzgFWYtmY4uHpNzLn3h9nVXNa: 'UXD Protocol Governance', + 'Strangemood Foundation Governance Program', } // Well known program names displayed on the instruction card export const PROGRAM_NAMES = { TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA: 'Token Program', '11111111111111111111111111111111': 'System Program', - mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68: 'Mango v3', - '9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin': 'Serum v3', - DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY: 'Serum v3 (devnet)', + mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68: 'Mango v3 Program', + '9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin': 'Serum v3 Program', + DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY: 'Serum v3 Program (devnet)', BPFLoaderUpgradeab1e11111111111111111111111: 'BPF Upgradeable Loader', @@ -29,7 +29,8 @@ export const PROGRAM_NAMES = { SysvarRent111111111111111111111111111111111: 'Sysvar: Rent', SysvarC1ock11111111111111111111111111111111: 'Sysvar: Clock', - '4Q6WW2ouZ6V3iaNm56MTd5n2tnTm4C5fiH8miFHnAFHo': 'Voter Stake Registry', + '4Q6WW2ouZ6V3iaNm56MTd5n2tnTm4C5fiH8miFHnAFHo': + 'Mango Voter Stake Registry Program', ...GOVERNANCE_PROGRAM_NAMES, } diff --git a/components/instructions/programs/solend.tsx b/components/instructions/programs/solend.tsx index c995115b0e..8309508045 100644 --- a/components/instructions/programs/solend.tsx +++ b/components/instructions/programs/solend.tsx @@ -24,8 +24,8 @@ export const SOLEND_PROGRAM_INSTRUCTIONS = { const mint = accounts[2].pubkey.toString() return ( -
-
+
+
Init obligation for: {mint}
@@ -51,20 +51,18 @@ export const SOLEND_PROGRAM_INSTRUCTIONS = { const reserveAccounts = accounts.slice(2) const reserveNames = reserveAccounts.map( - (x) => - SolendConfiguration.getTokenNameByReservePublicKey(x.pubkey) ?? - 'unknown' + (reserveAcc) => + SolendConfiguration.getTokenNameByReservePublicKey( + reserveAcc.pubkey + ) ?? 'unknown' ) return ( -
- {reserveNames.map((x, xi) => ( -
- Reserve #{xi + 1} - {x} +
+ {reserveNames.map((reserveName, reserveNameIdx) => ( +
+ Reserve #{reserveNameIdx + 1} + {reserveName}
))}
@@ -87,7 +85,7 @@ export const SOLEND_PROGRAM_INSTRUCTIONS = { 'unknown' return ( -
+
Reserve {tokenName}
@@ -118,14 +116,9 @@ export const SOLEND_PROGRAM_INSTRUCTIONS = { data: Uint8Array, accounts: AccountMetaData[] ) => { - console.log( - 'accounts', - accounts.map((x) => x.pubkey.toString()) - ) - const dataLayout = struct([u8('instruction'), nu64('liquidityAmount')]) - const args = dataLayout.decode(Buffer.from(data)) as any + const { liquidityAmount } = dataLayout.decode(Buffer.from(data)) as any const reserve = accounts[2] @@ -134,14 +127,14 @@ export const SOLEND_PROGRAM_INSTRUCTIONS = { 'unknown' return ( -
-
+
+
Token {tokenName}
-
+
Amount - {args.liquidityAmount} + {liquidityAmount}
) @@ -173,7 +166,7 @@ export const SOLEND_PROGRAM_INSTRUCTIONS = { ) => { const dataLayout = struct([u8('instruction'), nu64('collateralAmount')]) - const args = dataLayout.decode(Buffer.from(data)) as any + const { collateralAmount } = dataLayout.decode(Buffer.from(data)) as any const reserve = accounts[2] @@ -181,17 +174,15 @@ export const SOLEND_PROGRAM_INSTRUCTIONS = { SolendConfiguration.getTokenNameByReservePublicKey(reserve.pubkey) ?? 'unknown' - console.log('args', args) - return ( -
-
+
+
Token {tokenName}
-
+
Amount - {args.collateralAmount} + {collateralAmount}
) diff --git a/components/instructions/programs/system.tsx b/components/instructions/programs/system.tsx index a1973ed2d0..8562741a71 100644 --- a/components/instructions/programs/system.tsx +++ b/components/instructions/programs/system.tsx @@ -1,10 +1,10 @@ +import BufferLayout from 'buffer-layout' import { Connection, PublicKey } from '@solana/web3.js' import { AccountMetaData } from '@solana/spl-governance' import { BN } from '@project-serum/anchor' import { getMintDecimalAmountFromNatural } from '@tools/sdk/units' import { tryGetMint } from '@utils/tokens' import { WSOL_MINT } from '../tools' -import BufferLayout from 'buffer-layout' const SYSTEM_PROGRAM_ID = new PublicKey('11111111111111111111111111111111') @@ -78,7 +78,7 @@ export const SYSTEM_INSTRUCTIONS = { ) }, }, - [CREATE_ACCOUNT_WITH_SEED_INSTRUCTION_ID]: { + 3: { name: 'System Program - Create Account With Seed', accounts: [ 'Obligation', @@ -93,11 +93,7 @@ export const SYSTEM_INSTRUCTIONS = { _data: Uint8Array, _accounts: AccountMetaData[] ) => { - return ( - <> -

No data

- - ) + return

No Instruction data

}, }, }, diff --git a/components/instructions/programs/voteStakeRegistry.tsx b/components/instructions/programs/voteStakeRegistry.tsx index e83efca5a7..13030f5358 100644 --- a/components/instructions/programs/voteStakeRegistry.tsx +++ b/components/instructions/programs/voteStakeRegistry.tsx @@ -1,6 +1,6 @@ import { VsrClient } from '@blockworks-foundation/voter-stake-registry-client' -import { BN, Provider } from '@project-serum/anchor' import { Wallet } from '@project-serum/sol-wallet-adapter' +import { BN, Provider } from '@project-serum/anchor' import { AccountMetaData } from '@solana/spl-governance' import { Connection, Keypair, PublicKey } from '@solana/web3.js' import { fmtMintAmount } from '@tools/sdk/units' diff --git a/components/instructions/tools.tsx b/components/instructions/tools.tsx index 744a1dedf8..59e79d92ab 100644 --- a/components/instructions/tools.tsx +++ b/components/instructions/tools.tsx @@ -20,6 +20,8 @@ import { ATA_PROGRAM_INSTRUCTIONS } from './programs/associatedTokenAccount' import { SYSTEM_INSTRUCTIONS } from './programs/system' import { VOTE_STAKE_REGISTRY_INSTRUCTIONS } from './programs/voteStakeRegistry' import { MARINADE_INSTRUCTIONS } from './programs/marinade' +import { SOLEND_PROGRAM_INSTRUCTIONS } from './programs/solend' +import { ATA_PROGRAM_INSTRUCTIONS } from './programs/associatedTokenAccount' /** * Default governance program id instance */ @@ -41,6 +43,11 @@ export const ACCOUNT_NAMES = { 'Mango DAO USDC Treasury Vault', '65u1A86RC2U6whcHeD2mRG1tXCSmH2GsiktmEFQmzZgq': 'Mango DAO USDC Treasury Governance', + '4WQSYg21RrJNYhF4251XFpoy1uYbMHcMfZNLMXA3x5Mp': + 'Mango DAO Voter Stake Registry Registrar', + DPiH3H3c7t47BMxqTxLsuPQpEC6Kne8GA9VXbxpnZxFE: 'Mango DAO Governance Realm', + '7Sn4TN4ZkMghVBAhZ88UkyzXoYkMScaE6qtk9eWV3rJz': + 'Mango DAO Governance Realm Authority', '59BEyxwrFpt3x4sZ7TcXC3bHx3seGfqGkATcDx6siLWy': 'Mango v3 Insurance Fund Vault', '9qFV99WD5TKnpYw8w3xz3mgMBR5anoSZo2BynrGmNZqY': 'Mango v3 Revenue Vault', @@ -48,6 +55,9 @@ export const ACCOUNT_NAMES = { CF8sDcPztLDkvnEbYnCaXiDxhUpZ2uKLStpmFfRDNxSd: 'Mango v3 BTC-PERP Incentive Vault', '7Gm5zF6FNJpyhqdwKcEdMQw3r5YzitYUGVDKYMPT1cMy': 'Mango v3 Program Governance', + MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac: 'MNGO Token Mint', + EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: 'USDC Token Mint', + MyHd6a7HWKTMeJMHBkrbMq4hZwZxwn9x7dxXcopQ4Wd: 'OMH Token', '2A7UgheVhmoQqXBAQyG1wCoMpooPuiUf2DK6XFiQTtbG': 'OMH Mint Governance', @@ -159,6 +169,8 @@ export const INSTRUCTION_DESCRIPTORS = { ...SOLEND_PROGRAM_INSTRUCTIONS, ...ATA_PROGRAM_INSTRUCTIONS, ...MARINADE_INSTRUCTIONS, + ...SOLEND_PROGRAM_INSTRUCTIONS, + ...ATA_PROGRAM_INSTRUCTIONS, ...SYSTEM_INSTRUCTIONS, ...VOTE_STAKE_REGISTRY_INSTRUCTIONS, } @@ -180,7 +192,6 @@ export async function getInstructionDescriptor( const descriptor = !instruction.data.length ? descriptors : descriptors && descriptors[instruction.data[0]] - const dataUI = (descriptor?.getDataUI && (await descriptor?.getDataUI( connection, diff --git a/hooks/handleGovernanceAssetsStore.ts b/hooks/handleGovernanceAssetsStore.ts new file mode 100644 index 0000000000..af5eb1a20f --- /dev/null +++ b/hooks/handleGovernanceAssetsStore.ts @@ -0,0 +1,95 @@ +import { + DEFAULT_NATIVE_SOL_MINT, + DEFAULT_NFT_TREASURY_MINT, +} from '@components/instructions/tools' +import { + getNativeTreasuryAddress, + GovernanceAccountType, +} from '@solana/spl-governance' +import { ParsedAccountData } from '@solana/web3.js' +import { AccountInfoGen, GovernedTokenAccount } from '@utils/tokens' +import { useEffect } from 'react' +import useGovernanceAssetsStore from 'stores/useGovernanceAssetsStore' +import useWalletStore from 'stores/useWalletStore' +import useGovernanceAssets from './useGovernanceAssets' +import useRealm from './useRealm' + +export default function handleGovernanceAssetsStore() { + const { governances, tokenMints, realmTokenAccounts, realm } = useRealm() + const connection = useWalletStore((s) => s.connection.current) + const { getGovernancesByAccountTypes } = useGovernanceAssets() + const tokenGovernances = getGovernancesByAccountTypes([ + GovernanceAccountType.TokenGovernanceV1, + GovernanceAccountType.TokenGovernanceV2, + ]) + const { + setGovernancesArray, + setGovernedTokenAccounts, + } = useGovernanceAssetsStore() + useEffect(() => { + setGovernancesArray(governances) + }, [JSON.stringify(governances)]) + useEffect(() => { + async function prepareTokenGovernances() { + const governedTokenAccountsArray: GovernedTokenAccount[] = [] + + for (const gov of tokenGovernances) { + const realmTokenAccount = realmTokenAccounts.find( + (x) => + x.publicKey.toBase58() === gov.account.governedAccount.toBase58() + ) + const mint = tokenMints.find( + (x) => + realmTokenAccount?.account.mint.toBase58() === + x.publicKey.toBase58() + ) + const isNft = mint?.publicKey.toBase58() === DEFAULT_NFT_TREASURY_MINT + const isSol = mint?.publicKey.toBase58() === DEFAULT_NATIVE_SOL_MINT + let transferAddress = realmTokenAccount + ? realmTokenAccount.publicKey + : null + let solAccount: null | AccountInfoGen = null + if (isNft) { + transferAddress = gov.pubkey + } + if (isSol) { + const solAddress = await getNativeTreasuryAddress( + realm!.owner, + gov.pubkey + ) + transferAddress = solAddress + const resp = await connection.getParsedAccountInfo(solAddress) + const mintRentAmount = await connection.getMinimumBalanceForRentExemption( + 0 + ) + if (resp.value) { + solAccount = resp.value as AccountInfoGen< + Buffer | ParsedAccountData + > + solAccount.lamports = + solAccount.lamports !== 0 + ? solAccount.lamports - mintRentAmount + : solAccount.lamports + } + } + const obj = { + governance: gov, + token: realmTokenAccount, + mint, + isNft, + isSol, + transferAddress, + solAccount, + } + governedTokenAccountsArray.push(obj) + } + setGovernedTokenAccounts(governedTokenAccountsArray) + } + prepareTokenGovernances() + }, [ + JSON.stringify(tokenMints), + JSON.stringify(realmTokenAccounts), + JSON.stringify(tokenGovernances), + JSON.stringify(governances), + ]) +} diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 68aeb309fb..f64e0dde87 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -1,36 +1,24 @@ -import { - DEFAULT_NATIVE_SOL_MINT, - DEFAULT_NFT_TREASURY_MINT, - HIDDEN_GOVERNANCES, -} from '@components/instructions/tools' -import { - getNativeTreasuryAddress, - GovernanceAccountType, -} from '@solana/spl-governance' +import { GovernanceAccountType } from '@solana/spl-governance' import { MintInfo } from '@solana/spl-token' -import { ParsedAccountData } from '@solana/web3.js' import { - AccountInfoGen, getMultipleAccountInfoChunked, GovernedMintInfoAccount, - GovernedTokenAccount, parseMintAccountData, } from '@utils/tokens' import { Instructions } from '@utils/uiTypes/proposalCreationTypes' -import { useEffect, useState } from 'react' + import useWalletStore from 'stores/useWalletStore' + import useRealm from './useRealm' +import useGovernanceAssetsStore from 'stores/useGovernanceAssetsStore' export default function useGovernanceAssets() { - const { governances, tokenMints, realmTokenAccounts } = useRealm() + const { ownVoterWeight, realm, symbol, governances } = useRealm() const connection = useWalletStore((s) => s.connection.current) - const [governedTokenAccounts, setGovernedTokenAccounts] = useState< - GovernedTokenAccount[] - >([]) - const { ownVoterWeight, realm, symbol } = useRealm() - const governancesArray = Object.keys(governances) - .filter((gpk) => !HIDDEN_GOVERNANCES.has(gpk)) - .map((key) => governances[key]) + const governedTokenAccounts = useGovernanceAssetsStore( + (s) => s.governedTokenAccounts + ) + const governancesArray = useGovernanceAssetsStore((s) => s.governancesArray) const getGovernancesByAccountType = (type: GovernanceAccountType) => { const governancesFiltered = governancesArray.filter( @@ -49,24 +37,18 @@ export default function useGovernanceAssets() { function canUseGovernanceForInstruction(types: GovernanceAccountType[]) { return ( realm && - getGovernancesByAccountTypes(types).some((g) => - ownVoterWeight.canCreateProposal(g.account.config) + getGovernancesByAccountTypes(types).some((govAcc) => + ownVoterWeight.canCreateProposal(govAcc.account.config) ) ) } - const tokenGovernances = getGovernancesByAccountTypes([ - GovernanceAccountType.TokenGovernanceV1, - GovernanceAccountType.TokenGovernanceV2, - ]) const canMintRealmCommunityToken = () => { const governances = getGovernancesByAccountTypes([ GovernanceAccountType.MintGovernanceV1, GovernanceAccountType.MintGovernanceV2, ]) - return !!governances.find( - (x) => - x.account.governedAccount.toBase58() == - realm?.account.communityMint.toBase58() + return !!governances.find((govAcc) => + realm?.account.communityMint.equals(govAcc.account.governedAccount) ) } const canMintRealmCouncilToken = () => { @@ -106,12 +88,12 @@ export default function useGovernanceAssets() { const canUseAnyInstruction = realm && - governancesArray.some((g) => - ownVoterWeight.canCreateProposal(g.account.config) + governancesArray.some((gov) => + ownVoterWeight.canCreateProposal(gov.account.config) ) const getAvailableInstructions = () => { - return availableInstructions.filter((x) => x.isVisible) + return availableInstructions.filter((itx) => itx.isVisible) } async function getMintWithGovernances() { @@ -145,11 +127,13 @@ export default function useGovernanceAssets() { const governedTokenAccountsWithoutNfts = governedTokenAccounts.filter( (x) => !x.isNft ) - const nftsGovernedTokenAccounts = governedTokenAccounts.filter((x) => x.isNft) + const nftsGovernedTokenAccounts = governedTokenAccounts.filter( + (govTokenAcc) => govTokenAcc.isNft + ) const canUseTokenTransferInstruction = governedTokenAccountsWithoutNfts.some( - (g) => - g.governance && - ownVoterWeight.canCreateProposal(g.governance?.account?.config) + (acc) => + acc.governance && + ownVoterWeight.canCreateProposal(acc.governance?.account?.config) ) const availableInstructions = [ @@ -247,6 +231,41 @@ export default function useGovernanceAssets() { canUseTokenTransferInstruction && realm?.account.config.useCommunityVoterWeightAddin, }, + { + id: Instructions.CreateAssociatedTokenAccount, + name: 'Create Associated Token Account', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.CreateSolendObligationAccount, + name: 'Solend: Create Obligation Account', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.InitSolendObligationAccount, + name: 'Solend: Init Obligation Account', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.DepositReserveLiquidityAndObligationCollateral, + name: 'Solend: Deposit Funds', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.RefreshSolendReserve, + name: 'Solend: Refresh Reserve', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.RefreshSolendObligation, + name: 'Solend: Refresh Obligation', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.WithdrawObligationCollateralAndRedeemReserveLiquidity, + name: 'Solend: Withdraw Funds', + isVisible: canUseAnyInstruction, + }, { id: Instructions.ProgramUpgrade, name: 'Upgrade Program', @@ -287,69 +306,6 @@ export default function useGovernanceAssets() { ), }, ] - useEffect(() => { - async function prepareTokenGovernances() { - const governedTokenAccountsArray: GovernedTokenAccount[] = [] - - for (const gov of tokenGovernances) { - const realmTokenAccount = realmTokenAccounts.find( - (x) => - x.publicKey.toBase58() === gov.account.governedAccount.toBase58() - ) - const mint = tokenMints.find( - (x) => - realmTokenAccount?.account.mint.toBase58() === - x.publicKey.toBase58() - ) - const isNft = mint?.publicKey.toBase58() === DEFAULT_NFT_TREASURY_MINT - const isSol = mint?.publicKey.toBase58() === DEFAULT_NATIVE_SOL_MINT - let transferAddress = realmTokenAccount - ? realmTokenAccount.publicKey - : null - let solAccount: null | AccountInfoGen = null - if (isNft) { - transferAddress = gov.pubkey - } - if (isSol) { - const solAddress = await getNativeTreasuryAddress( - realm!.owner, - gov.pubkey - ) - transferAddress = solAddress - const resp = await connection.getParsedAccountInfo(solAddress) - const mintRentAmount = await connection.getMinimumBalanceForRentExemption( - 0 - ) - if (resp.value) { - solAccount = resp.value as AccountInfoGen< - Buffer | ParsedAccountData - > - solAccount.lamports = - solAccount.lamports !== 0 - ? solAccount.lamports - mintRentAmount - : solAccount.lamports - } - } - const obj = { - governance: gov, - token: realmTokenAccount, - mint, - isNft, - isSol, - transferAddress, - solAccount, - } - governedTokenAccountsArray.push(obj) - } - setGovernedTokenAccounts(governedTokenAccountsArray) - } - prepareTokenGovernances() - }, [ - JSON.stringify(tokenMints), - JSON.stringify(realmTokenAccounts), - JSON.stringify(tokenGovernances), - JSON.stringify(governances), - ]) return { governancesArray, getGovernancesByAccountType, diff --git a/hooks/useGovernedMultiTypeAccounts.ts b/hooks/useGovernedMultiTypeAccounts.ts index 1af34eba58..acb2f4f23a 100644 --- a/hooks/useGovernedMultiTypeAccounts.ts +++ b/hooks/useGovernedMultiTypeAccounts.ts @@ -19,15 +19,15 @@ export default function useGovernedMultiTypeAccounts() { const mintWithGovernances = await getMintWithGovernances() return governancesArray.map((gov) => { - const governedTokenAccount = governedTokenAccounts.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() + const governedTokenAccount = governedTokenAccounts.find((tokenAcc) => + tokenAcc.governance?.pubkey.equals(gov.pubkey) ) if (governedTokenAccount) { return governedTokenAccount as GovernedMultiTypeAccount } - const mintGovernance = mintWithGovernances.find( - (x) => x.governance?.pubkey.toBase58() === gov.pubkey.toBase58() + const mintGovernance = mintWithGovernances.find((mint) => + mint.governance?.pubkey.equals(gov.pubkey) ) if (mintGovernance) { return mintGovernance as GovernedMultiTypeAccount diff --git a/hooks/useIsBeyondTimestamp.ts b/hooks/useIsBeyondTimestamp.ts index 5f24dd40cc..bc0b8db73e 100644 --- a/hooks/useIsBeyondTimestamp.ts +++ b/hooks/useIsBeyondTimestamp.ts @@ -1,4 +1,4 @@ -import moment from 'moment' +import dayjs from 'dayjs' import { useEffect, useState } from 'react' export const useIsBeyondTimestamp = (timestamp: number | undefined) => { @@ -12,7 +12,7 @@ export const useIsBeyondTimestamp = (timestamp: number | undefined) => { } const sub = (async () => { - const now = moment().unix() + const now = dayjs().unix() if (now > timestamp) { setIsBeyondTimestamp(true) @@ -22,7 +22,7 @@ export const useIsBeyondTimestamp = (timestamp: number | undefined) => { setIsBeyondTimestamp(false) const id = setInterval(() => { - const now = moment().unix() + const now = dayjs().unix() if (now > timestamp) { setIsBeyondTimestamp(true) clearInterval(id) diff --git a/hooks/useProposalVotes.tsx b/hooks/useProposalVotes.tsx index 76a7b9b839..27690e2f0c 100644 --- a/hooks/useProposalVotes.tsx +++ b/hooks/useProposalVotes.tsx @@ -24,6 +24,7 @@ export default function useProposalVotes(proposal?: Proposal) { yesVoteCount: 0, noVoteCount: 0, minimumYesVotes: 0, + yesVotesRequired: 0, } const voteThresholdPct = @@ -59,6 +60,8 @@ export default function useProposalVotes(proposal?: Proposal) { const relativeYesVotes = getRelativeVoteCount(yesVoteCount) const relativeNoVotes = getRelativeVoteCount(noVoteCount) + const yesVotesRequired = Math.max(minimumYesVotes - yesVoteCount, 1) + return { voteThresholdPct, yesVotePct, @@ -68,5 +71,6 @@ export default function useProposalVotes(proposal?: Proposal) { relativeYesVotes, relativeNoVotes, minimumYesVotes, + yesVotesRequired, } } diff --git a/package.json b/package.json index 60c1d76c98..ad9cb39510 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "lint": "eslint . --ext ts --ext tsx --ext js --ext jsx", "test": "jest", "test-all": "yarn lint && yarn type-check && yarn test", - "postinstall": "patch-package", "notifier": "ts-node scripts/governance-notifier.ts" }, "lint-staged": { @@ -31,10 +30,9 @@ "@headlessui/react": "^1.4.2", "@heroicons/react": "^1.0.1", "@marinade.finance/marinade-ts-sdk": "^2.0.9", - "@metaplex/js": "^4.10.1", "@nfteyez/sol-rayz": "^0.8.0", - "@project-serum/anchor": "^0.19.0", "@project-serum/borsh": "^0.2.2", + "@project-serum/anchor": "^0.20.1", "@project-serum/common": "^0.0.1-beta.3", "@project-serum/sol-wallet-adapter": "^0.2.0", "@raydium-io/raydium-sdk": "^1.0.1-beta.26", @@ -45,15 +43,15 @@ "@solana/wallet-adapter-phantom": "^0.7.0", "@solana/wallet-adapter-sollet": "^0.8.0", "@solana/web3.js": "^1.31.0", - "@solendprotocol/solend-sdk": "^0.4.7", + "@solendprotocol/solend-sdk": "^0.4.9", "@tippyjs/react": "^4.2.5", "@uxdprotocol/uxd-client": "4.10.0", "axios": "^0.21.1", "bignumber.js": "^9.0.1", "buffer-layout": "^1.2.2", "classnames": "^2.3.1", + "dayjs": "^1.10.7", "immer": "^9.0.1", - "moment": "^2.29.1", "next": "^12.0.4", "next-themes": "^0.0.14", "next-transpile-modules": "^8.0.0", @@ -65,13 +63,12 @@ "react-portal": "^4.2.1", "react-virtualized": "^9.22.3", "superstruct": "^0.15.3", - "ts-node": "^10.4.0", + "ts-node": "^10.5.0", "tsconfig-paths": "^3.12.0", "yup": "^0.32.11", "zustand": "^3.4.1" }, "devDependencies": { - "@emotion/babel-preset-css-prop": "^11.2.0", "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^11.2.5", "@types/jest": "^27.0.3", @@ -85,7 +82,6 @@ "eslint-plugin-react": "^7.27.1", "eslint-plugin-react-hooks": "^4.3.0", "husky": "^6.0.0", - "identity-obj-proxy": "^3.0.0", "jest": "^27.4.5", "lint-staged": "^10.0.10", "next-router-mock": "^0.6.3", diff --git a/pages/_app.tsx b/pages/_app.tsx index 9add7324f9..827f34a5f5 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -18,12 +18,14 @@ import ErrorBoundary from '@components/ErrorBoundary' import { WalletIdentityProvider } from '@cardinal/namespaces-components' import useVoteStakeRegistryClientStore from 'VoteStakeRegistry/stores/voteStakeRegistryClientStore' import useMarketStore from 'Strategies/store/marketStore' +import handleGovernanceAssetsStore from '@hooks/handleGovernanceAssetsStore' function App({ Component, pageProps }) { useHydrateStore() useWallet() useRouterHistory() useVoteRegistry() + handleGovernanceAssetsStore() const { loadMarket } = useMarketStore() const { getOwnedDeposits, resetDepositState } = useDepositStore() const { realm, realmInfo, symbol, ownTokenRecord } = useRealm() diff --git a/pages/dao/[symbol]/index.tsx b/pages/dao/[symbol]/index.tsx index 924ae69482..391ca4dc0f 100644 --- a/pages/dao/[symbol]/index.tsx +++ b/pages/dao/[symbol]/index.tsx @@ -3,7 +3,12 @@ import useRealm from 'hooks/useRealm' import React, { useEffect, useState } from 'react' import ProposalFilter from 'components/ProposalFilter' import ProposalCard from 'components/ProposalCard' -import { Proposal, ProposalState } from '@solana/spl-governance' +import { + Governance, + ProgramAccount, + Proposal, + ProposalState, +} from '@solana/spl-governance' import NewProposalBtn from './proposal/components/NewProposalBtn' import RealmHeader from 'components/RealmHeader' import { PublicKey } from '@solana/web3.js' @@ -17,7 +22,13 @@ import { usePrevious } from '@hooks/usePrevious' import TokenBalanceCardWrapper from '@components/TokenBalance/TokenBalanceCardWrapper' import ApproveAllBtn from './proposal/components/ApproveAllBtn' -const compareProposals = (p1: Proposal, p2: Proposal) => { +const compareProposals = ( + p1: Proposal, + p2: Proposal, + governances: { + [governance: string]: ProgramAccount + } +) => { const p1Rank = p1.getStateSortRank() const p2Rank = p2.getStateSortRank() @@ -27,10 +38,33 @@ const compareProposals = (p1: Proposal, p2: Proposal) => { return -1 } - const tsCompare = p1.getStateTimestamp() - p2.getStateTimestamp() + if (p1.state === ProposalState.Voting && p2.state === ProposalState.Voting) { + const p1VotingRank = getVotingStateRank(p1, governances) + const p2VotingRank = getVotingStateRank(p2, governances) + + if (p1VotingRank > p2VotingRank) { + return 1 + } else if (p1VotingRank < p2VotingRank) { + return -1 + } + + // Show the proposals in voting state expiring earlier at the top + return p2.getStateTimestamp() - p1.getStateTimestamp() + } + + return p1.getStateTimestamp() - p2.getStateTimestamp() +} - // Show the proposals in voting state expiring earlier at the top - return p1.state === ProposalState.Voting ? ~tsCompare : tsCompare +/// Compares proposals in voting state to distinguish between Voting and Finalizing states +function getVotingStateRank( + proposal: Proposal, + governances: { + [governance: string]: ProgramAccount + } +) { + // Show proposals in Voting state before proposals in Finalizing state + const governance = governances[proposal.governance.toBase58()].account + return proposal.hasVoteTimeEnded(governance) ? 0 : 1 } const REALM = () => { @@ -40,6 +74,7 @@ const REALM = () => { proposals, realmTokenAccount, ownTokenRecord, + governances, } = useRealm() const { nftsGovernedTokenAccounts } = useGovernanceAssets() const prevStringifyNftsGovernedTokenAccounts = usePrevious( @@ -55,7 +90,7 @@ const REALM = () => { const wallet = useWalletStore((s) => s.current) const allProposals = Object.entries(proposals).sort((a, b) => - compareProposals(b[1].account, a[1].account) + compareProposals(b[1].account, a[1].account, governances) ) useEffect(() => { diff --git a/pages/dao/[symbol]/proposal/[pk].tsx b/pages/dao/[symbol]/proposal/[pk].tsx index b02feebac7..da3b5eb416 100644 --- a/pages/dao/[symbol]/proposal/[pk].tsx +++ b/pages/dao/[symbol]/proposal/[pk].tsx @@ -28,7 +28,7 @@ const Proposal = () => { const { symbol, realmInfo } = useRealm() const { proposal, descriptionLink } = useProposal() const [description, setDescription] = useState('') - const { yesVoteProgress, yesVoteCount, minimumYesVotes } = useProposalVotes( + const { yesVoteProgress, yesVotesRequired } = useProposalVotes( proposal?.account ) @@ -128,7 +128,7 @@ const Proposal = () => { {proposal?.account.state === ProposalState.Voting ? (
diff --git a/pages/dao/[symbol]/proposal/components/ApproveAllBtn.tsx b/pages/dao/[symbol]/proposal/components/ApproveAllBtn.tsx index 5c58f3a0aa..f2599c5626 100644 --- a/pages/dao/[symbol]/proposal/components/ApproveAllBtn.tsx +++ b/pages/dao/[symbol]/proposal/components/ApproveAllBtn.tsx @@ -47,11 +47,8 @@ const ApproveAllBtn = () => { const canApproveProposals = connected && votingProposals.length > 0 - console.log(ownVoteRecordsByProposal) - const approveAll = async () => { if (!wallet || !programId || !realm) return - console.log(ownVoteRecordsByProposal) const governanceAuthority = wallet.publicKey! const payer = wallet.publicKey! diff --git a/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx index 41f95b7353..7ae6fb92f5 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx @@ -1,25 +1,28 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - CreateAssociatedTokenAccountForm, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../new' -import useWalletStore from 'stores/useWalletStore' -import Select from '@components/inputs/Select' + import { + Governance, ProgramAccount, serializeInstructionToBase64, - Governance, } from '@solana/spl-governance' -import GovernedAccountSelect from '../GovernedAccountSelect' +import { PublicKey } from '@solana/web3.js' +import Select from '@components/inputs/Select' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' +import useRealm from '@hooks/useRealm' import { createAssociatedTokenAccount } from '@utils/associated' +import { isFormValid } from '@utils/formValidation' import { getSplTokenMintAddressByUIName, SPL_TOKENS } from '@utils/splTokens' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' +import { + CreateAssociatedTokenAccountForm, + UiInstruction, +} from '@utils/uiTypes/proposalCreationTypes' + +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import useWalletStore from 'stores/useWalletStore' + +import { NewProposalContext } from '../../new' +import GovernedAccountSelect from '../GovernedAccountSelect' const CreateAssociatedTokenAccount = ({ index, @@ -79,19 +82,6 @@ const CreateAssociatedTokenAccount = ({ getSplTokenMintAddressByUIName(form.splTokenMintUIName) ) - console.log('infos', { - governanceAccount: form.governedAccount.governance.account, - governanceOwner: form.governedAccount.governance.owner.toString(), - fundingAddress: wallet.publicKey.toString(), - walletAddress: form.governedAccount.governance.pubkey.toString(), - splTokenMintName: form.splTokenMintUIName, - splTokenMintAddress: getSplTokenMintAddressByUIName( - form.splTokenMintUIName - ).toString(), - }) - - console.log('tx', tx) - return { serializedInstruction: serializeInstructionToBase64(tx), isValid: true, @@ -104,7 +94,7 @@ const CreateAssociatedTokenAccount = ({ propertyName: 'programId', value: programId?.toString(), }) - }, [realmInfo?.programId]) + }, [programId]) useEffect(() => { handleSetInstructions( @@ -136,7 +126,7 @@ const CreateAssociatedTokenAccount = ({ error={formErrors['governedAccount']} shouldBeGoverned={shouldBeGoverned} governance={governance} - > + /> | null }) => { - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - const shouldBeGoverned = index !== 0 && governance const [form, setForm] = useState({ governedAccount: undefined, }) + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() + const shouldBeGoverned = index !== 0 && governance const [formErrors, setFormErrors] = useState({}) const { handleSetInstructions } = useContext(NewProposalContext) const handleSetForm = ({ propertyName, value }) => { @@ -52,19 +50,17 @@ const Empty = ({ .required('Governed account is required'), }) return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx index fce5942933..5f8ce2ed40 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx @@ -1,23 +1,25 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' +import { + Governance, + ProgramAccount, + serializeInstructionToBase64, +} from '@solana/spl-governance' import * as yup from 'yup' +import { PublicKey } from '@solana/web3.js' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' +import useRealm from '@hooks/useRealm' +import { createObligationAccount } from '@tools/sdk/solend/createObligationAccount' import { isFormValid } from '@utils/formValidation' import { - UiInstruction, CreateSolendObligationAccountForm, + UiInstruction, } from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' + +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import useWalletStore from 'stores/useWalletStore' -import { - ProgramAccount, - serializeInstructionToBase64, - Governance, -} from '@solana/spl-governance' + +import { NewProposalContext } from '../../../new' import GovernedAccountSelect from '../../GovernedAccountSelect' -import { createObligationAccount } from '@tools/sdk/solend/createObligationAccount' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' const CreateObligationAccount = ({ index, @@ -29,13 +31,12 @@ const CreateObligationAccount = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} } - + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({}) @@ -87,7 +88,7 @@ const CreateObligationAccount = ({ propertyName: 'programId', value: programId?.toString(), }) - }, [realmInfo?.programId]) + }, [programId]) useEffect(() => { handleSetInstructions( @@ -107,19 +108,17 @@ const CreateObligationAccount = ({ }) return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx index cf8ef1d293..ac6be4ce2d 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx @@ -1,30 +1,29 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' +import BigNumber from 'bignumber.js' import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - DepositReserveLiquidityAndObligationCollateralForm, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' -import useWalletStore from 'stores/useWalletStore' + +import { BN } from '@project-serum/anchor' import { + Governance, ProgramAccount, serializeInstructionToBase64, - Governance, } from '@solana/spl-governance' -import GovernedAccountSelect from '../../GovernedAccountSelect' +import { PublicKey } from '@solana/web3.js' import Input from '@components/inputs/Input' -import { depositReserveLiquidityAndObligationCollateral } from '@tools/sdk/solend/depositReserveLiquidityAndObligationCollateral' import Select from '@components/inputs/Select' - -import SolendConfiguration from '@tools/sdk/solend/configuration' - -import BigNumber from 'bignumber.js' -import { BN } from '@project-serum/anchor' import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' +import useRealm from '@hooks/useRealm' +import SolendConfiguration from '@tools/sdk/solend/configuration' +import { depositReserveLiquidityAndObligationCollateral } from '@tools/sdk/solend/depositReserveLiquidityAndObligationCollateral' +import { isFormValid } from '@utils/formValidation' +import { + DepositReserveLiquidityAndObligationCollateralForm, + UiInstruction, +} from '@utils/uiTypes/proposalCreationTypes' +import useWalletStore from 'stores/useWalletStore' +import { NewProposalContext } from '../../../new' +import GovernedAccountSelect from '../../GovernedAccountSelect' const DepositReserveLiquidityAndObligationCollateral = ({ index, @@ -36,8 +35,8 @@ const DepositReserveLiquidityAndObligationCollateral = ({ const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} @@ -108,7 +107,7 @@ const DepositReserveLiquidityAndObligationCollateral = ({ propertyName: 'programId', value: programId?.toString(), }) - }, [realmInfo?.programId]) + }, [programId]) useEffect(() => { handleSetInstructions( @@ -144,7 +143,7 @@ const DepositReserveLiquidityAndObligationCollateral = ({ error={formErrors['governedAccount']} shouldBeGoverned={shouldBeGoverned} governance={governance} - > + /> { +import { NewProposalContext } from '../../../new' +import GovernedAccountSelect from '../../GovernedAccountSelect' + +const RefreshReserve = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { realmInfo } = useRealm() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({}) const [formErrors, setFormErrors] = useState({}) @@ -49,12 +66,13 @@ const RefreshReserve = ({ index }: { index: number }) => { !isValid || !programId || !form.mintName || + !form.governedAccount?.governance.account || !wallet?.publicKey ) { return { serializedInstruction: '', isValid: false, - governance: undefined, + governance: form.governedAccount?.governance, } } @@ -65,7 +83,7 @@ const RefreshReserve = ({ index }: { index: number }) => { return { serializedInstruction: serializeInstructionToBase64(tx), isValid: true, - governance: undefined, + governance: form.governedAccount?.governance, } } @@ -74,11 +92,12 @@ const RefreshReserve = ({ index }: { index: number }) => { propertyName: 'programId', value: programId?.toString(), }) - }, [realmInfo?.programId]) + }, [programId]) useEffect(() => { handleSetInstructions( { + governedAccount: form.governedAccount?.governance, getInstruction, }, index @@ -86,11 +105,26 @@ const RefreshReserve = ({ index }: { index: number }) => { }, [form]) const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), mintName: yup.string().required('Token Name is required'), }) return ( <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + /> + + + handleSetForm({ + value: evt.target.value, + propertyName: 'destinationLiquidity', + }) + } + error={formErrors['destinationLiquidity']} + /> ) } diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index 89daa4426a..5965ad33c6 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -15,7 +15,6 @@ import { RpcContext, } from '@solana/spl-governance' import { PublicKey } from '@solana/web3.js' - import Button, { LinkButton, SecondaryButton } from '@components/Button' import Input from '@components/inputs/Input' import Select from '@components/inputs/Select' @@ -50,21 +49,7 @@ import Empty from './components/instructions/Empty' import MakeChangeMaxAccounts from './components/instructions/Mango/MakeChangeMaxAccounts' import MakeChangeReferralFeeParams from './components/instructions/Mango/MakeChangeReferralFeeParams' import Mint from './components/instructions/Mint' -import AddLiquidityRaydium from './components/instructions/raydium/AddLiquidity' -import RemoveLiquidityRaydium from './components/instructions/raydium/RemoveLiquidity' -import CreateObligationAccount from './components/instructions/solend/CreateObligationAccount' -import DepositReserveLiquidityAndObligationCollateral from './components/instructions/solend/DepositReserveLiquidityAndObligationCollateral' -import InitObligationAccount from './components/instructions/solend/InitObligationAccount' -import RefreshObligation from './components/instructions/solend/RefreshObligation' -import RefreshReserve from './components/instructions/solend/RefreshReserve' -import WithdrawObligationCollateralAndRedeemReserveLiquidity from './components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity' import SplTokenTransfer from './components/instructions/SplTokenTransfer' -import DepositInsuranceToMangoDepository from './components/instructions/UXD/DepositInsuranceToMangoDepository' -import InitializeController from './components/instructions/UXD/InitializeController' -import RegisterMangoDepository from './components/instructions/UXD/RegisterMangoDepository' -import SetMangoDepositoriesRedeemableSoftCap from './components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap' -import SetRedeemGlobalSupplyCap from './components/instructions/UXD/SetRedeemGlobalSupplyCap' -import WithdrawInsuranceFromMangoDepository from './components/instructions/UXD/WithdrawInsuranceFromMangoDepository' import VoteBySwitch from './components/VoteBySwitch' const schema = yup.object().shape({ @@ -84,17 +69,9 @@ export const NewProposalContext = createContext( function extractGovernanceAccountFromInstructionsData( instructionsData: ComponentInstructionData[] ): ProgramAccount | null { - return instructionsData.reduce((tmp, x) => { - if (tmp) { - return tmp - } - - if (x.governedAccount) { - return x.governedAccount - } - - return tmp - }, null) + return ( + instructionsData.find((itx) => itx.governedAccount)?.governedAccount ?? null + ) } const New = () => { @@ -348,7 +325,7 @@ const New = () => { case Instructions.RefreshSolendObligation: return case Instructions.RefreshSolendReserve: - return + return case Instructions.WithdrawObligationCollateralAndRedeemReserveLiquidity: return ( { governance={governance} /> ) - case Instructions.AddLiquidityRaydium: - return - case Instructions.RemoveLiquidityRaydium: - return - case Instructions.InitializeController: - return - case Instructions.SetRedeemableGlobalSupplyCap: - return - case Instructions.SetMangoDepositoriesRedeemableSoftCap: - return ( - - ) - case Instructions.RegisterMangoDepository: - return - case Instructions.DepositInsuranceToMangoDepository: - return ( - - ) - case Instructions.WithdrawInsuranceFromMangoDepository: - return ( - - ) case Instructions.Mint: return case Instructions.Base64: diff --git a/patches/@project-serum+anchor+0.19.0.patch b/patches/@project-serum+anchor+0.19.0.patch deleted file mode 100644 index 45aa302cd1..0000000000 --- a/patches/@project-serum+anchor+0.19.0.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/node_modules/@project-serum/anchor/dist/esm/program/common.js b/node_modules/@project-serum/anchor/dist/esm/program/common.js -index 164d8ff..bab8614 100644 ---- a/node_modules/@project-serum/anchor/dist/esm/program/common.js -+++ b/node_modules/@project-serum/anchor/dist/esm/program/common.js -@@ -38,15 +38,6 @@ export function validateAccounts(ixAccounts, accounts = {}) { - } - // Translates an address to a Pubkey. - export function translateAddress(address) { -- if (typeof address === "string") { -- const pk = new PublicKey(address); -- return pk; -- } -- else if (address.constructor.prototype.constructor.name === "PublicKey") { -- return address; -- } -- else { -- throw new Error("Given address is not a PublicKey nor a string."); -- } -+ return new PublicKey(address); - } - //# sourceMappingURL=common.js.map -\ No newline at end of file diff --git a/public/realms/DAKU/img/daku_logo.png b/public/realms/DAKU/img/daku_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..aca6d6c2ab56a9afc59408f44ea7314a93e9379c GIT binary patch literal 120992 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

LK1efX6o9%qU@urPSh)XFIzY(W;YjDxD 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 filters )), } - // XXX: sleep to prevent public RPC rate limits - await sleep(3_000) + + // note: if we are not using a specific RPC, then most probably we are using a public RPC + // sleep to prevent public RPC rate limits + if (!process.env.CLUSTER_URL) { + await sleep(3_000) + } } return accounts diff --git a/scripts/governance-notifier.ts b/scripts/governance-notifier.ts index 1e1be8a1b9..1017cbe609 100644 --- a/scripts/governance-notifier.ts +++ b/scripts/governance-notifier.ts @@ -21,7 +21,8 @@ function errorWrapper() { async function runNotifier() { const nowInSeconds = new Date().getTime() / 1000 - const MAINNET_RPC_NODE = 'https://api.mainnet-beta.solana.com' + const MAINNET_RPC_NODE = + process.env.CLUSTER_URL || 'https://api.mainnet-beta.solana.com' const connectionContext = getConnectionContext('mainnet') const REALM_SYMBOL = process.env.REALM_SYMBOL || 'MNGO' diff --git a/stores/useGovernanceAssetsStore.tsx b/stores/useGovernanceAssetsStore.tsx new file mode 100644 index 0000000000..8c59ce9136 --- /dev/null +++ b/stores/useGovernanceAssetsStore.tsx @@ -0,0 +1,37 @@ +import create, { State } from 'zustand' +import { Governance } from '@solana/spl-governance' +import { ProgramAccount } from '@solana/spl-governance' +import { HIDDEN_GOVERNANCES } from '@components/instructions/tools' +import { GovernedTokenAccount } from '@utils/tokens' + +interface GovernanceAssetsStore extends State { + governancesArray: ProgramAccount[] + governedTokenAccounts: GovernedTokenAccount[] + setGovernancesArray: (governances: { + [governance: string]: ProgramAccount + }) => void + setGovernedTokenAccounts: (items: GovernedTokenAccount[]) => void +} + +const defaultState = { + governancesArray: [], + governedTokenAccounts: [], +} + +const useGovernanceAssetsStore = create((set, _get) => ({ + ...defaultState, + setGovernancesArray: (governances) => { + set((s) => { + s.governancesArray = Object.keys(governances) + .filter((gpk) => !HIDDEN_GOVERNANCES.has(gpk)) + .map((key) => governances[key]) + }) + }, + setGovernedTokenAccounts: (items) => { + set((s) => { + s.governedTokenAccounts = items + }) + }, +})) + +export default useGovernanceAssetsStore diff --git a/tools/sdk/solend/configuration.ts b/tools/sdk/solend/configuration.ts index 87441c4a9b..07f6af3f1e 100644 --- a/tools/sdk/solend/configuration.ts +++ b/tools/sdk/solend/configuration.ts @@ -79,25 +79,23 @@ class SolendConfiguration implements ASolendConfiguration { }, } - public get programID(): PublicKey { - return new PublicKey('So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo') - } - - public get lendingMarket(): PublicKey { - return new PublicKey('4UpD2fh7xH3VP9QQaXtsS1YY3bxzWhtfpks7FatyKvdY') - } - - public get lendingMarketAuthority(): PublicKey { - return new PublicKey('DdZR6zRFiUt4S5mg7AV1uKB2z1f1WzcNYCaTEEWPAuby') - } - - public get createObligationConfiguration() { - // All of theses numbers are magic numbers we got by looking at Solend documentation & transactions - return { - lamports: 9938880, - space: 1300, - seed: this.lendingMarket.toString().slice(0, 32), - } + public readonly programID = new PublicKey( + 'So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo' + ) + + public readonly lendingMarket = new PublicKey( + '4UpD2fh7xH3VP9QQaXtsS1YY3bxzWhtfpks7FatyKvdY' + ) + + public readonly lendingMarketAuthority = new PublicKey( + 'DdZR6zRFiUt4S5mg7AV1uKB2z1f1WzcNYCaTEEWPAuby' + ) + + // All of theses numbers are magic numbers we got by looking at Solend documentation & transactions + public readonly createObligationConfiguration = { + lamports: 9938880, + space: 1300, + seed: this.lendingMarket.toString().slice(0, 32), } public getSupportedCollateralMintsInformation(): SupportedCollateralMintsInformation { @@ -111,7 +109,9 @@ class SolendConfiguration implements ASolendConfiguration { } public getReserveOfGivenMints(mintNames: SupportedMintName[]): PublicKey[] { - return mintNames.map((x) => this.supportedMintsInformation[x].reserve) + return mintNames.map( + (mintName) => this.supportedMintsInformation[mintName].reserve + ) } public getSupportedMintNames(): SupportedMintName[] { @@ -129,7 +129,7 @@ class SolendConfiguration implements ASolendConfiguration { return tmp }, - void 0 + undefined ) } } diff --git a/tools/sdk/solend/depositReserveLiquidityAndObligationCollateral.ts b/tools/sdk/solend/depositReserveLiquidityAndObligationCollateral.ts index 504626d8f4..ff73cedfa4 100644 --- a/tools/sdk/solend/depositReserveLiquidityAndObligationCollateral.ts +++ b/tools/sdk/solend/depositReserveLiquidityAndObligationCollateral.ts @@ -1,12 +1,12 @@ import { BN } from '@project-serum/anchor' import { PublicKey } from '@solana/web3.js' import { depositReserveLiquidityAndObligationCollateralInstruction } from '@solendprotocol/solend-sdk' -import { findATAAddrSync } from '@uxdprotocol/uxd-client' import { SupportedMintName } from './configuration' import SolendConfiguration from './configuration' import { deriveObligationAddressFromWalletAndSeed } from './utils' +import { findATAAddrSync } from '@utils/ataTools' export async function depositReserveLiquidityAndObligationCollateral({ obligationOwner, diff --git a/tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity.ts b/tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity.ts index 36cdefe200..2208a7fc52 100644 --- a/tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity.ts +++ b/tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity.ts @@ -1,7 +1,7 @@ import { BN } from '@project-serum/anchor' import { PublicKey } from '@solana/web3.js' import { withdrawObligationCollateralAndRedeemReserveLiquidity as originalWithdrawFunction } from '@solendprotocol/solend-sdk' -import { findATAAddrSync } from '@uxdprotocol/uxd-client' +import { findATAAddrSync } from '@utils/ataTools' import SolendConfiguration, { SupportedMintName } from './configuration' import { deriveObligationAddressFromWalletAndSeed } from './utils' @@ -10,10 +10,12 @@ export async function withdrawObligationCollateralAndRedeemReserveLiquidity({ obligationOwner, liquidityAmount, mintName, + destinationLiquidity, }: { obligationOwner: PublicKey liquidityAmount: number | BN mintName: SupportedMintName + destinationLiquidity?: PublicKey }) { const { relatedCollateralMint, @@ -39,7 +41,6 @@ export async function withdrawObligationCollateralAndRedeemReserveLiquidity({ const sourceCollateral = reserveCollateralSupplySplTokenAccount const destinationCollateral = cusdcTokenAccount const withdrawReserve = reserve - const destinationLiquidity = usdcTokenAccount return originalWithdrawFunction( liquidityAmount, @@ -49,7 +50,7 @@ export async function withdrawObligationCollateralAndRedeemReserveLiquidity({ obligation, SolendConfiguration.lendingMarket, SolendConfiguration.lendingMarketAuthority, - destinationLiquidity, + destinationLiquidity ?? usdcTokenAccount, reserveCollateralMint, reserveLiquiditySupply, obligationOwner, diff --git a/utils/associated.tsx b/utils/associated.tsx index 9a5358004e..dff5854d56 100644 --- a/utils/associated.tsx +++ b/utils/associated.tsx @@ -6,7 +6,7 @@ import { } from '@solana/web3.js' import { TOKEN_PROGRAM_ID } from '@solana/spl-token' -const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID: PublicKey = new PublicKey( +export const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID: PublicKey = new PublicKey( 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL' ) diff --git a/utils/ataTools.tsx b/utils/ataTools.tsx index 89ae4eb775..7dac969795 100644 --- a/utils/ataTools.tsx +++ b/utils/ataTools.tsx @@ -1,3 +1,4 @@ +import { utils } from '@project-serum/anchor' import { ASSOCIATED_TOKEN_PROGRAM_ID, Token, @@ -86,3 +87,18 @@ export async function getATA({ needToCreateAta, } } + +export function findATAAddrSync( + wallet: PublicKey, + mintAddress: PublicKey +): [PublicKey, number] { + const seeds = [ + wallet.toBuffer(), + TOKEN_PROGRAM_ID.toBuffer(), + mintAddress.toBuffer(), + ] + return utils.publicKey.findProgramAddressSync( + seeds, + ASSOCIATED_TOKEN_PROGRAM_ID + ) +} diff --git a/utils/formatting.tsx b/utils/formatting.tsx index e3089b6abe..8466eb4f97 100644 --- a/utils/formatting.tsx +++ b/utils/formatting.tsx @@ -1,6 +1,7 @@ -import { BN } from '@project-serum/anchor' -import moment from 'moment' +import BN from 'bn.js' +import dayjs from 'dayjs' import { PublicKey } from '@solana/web3.js' +const relativeTime = require('dayjs/plugin/relativeTime') const votePrecision = 10000 export const calculatePct = (c: BN = new BN(0), total?: BN) => { @@ -20,7 +21,9 @@ export const calculatePct = (c: BN = new BN(0), total?: BN) => { export const fmtTokenAmount = (c: BN = new BN(0), decimals?: number) => c.div(new BN(10).pow(new BN(decimals ?? 0))).toNumber() -export const fmtUnixTime = (d: BN) => moment.unix(d.toNumber()).fromNow() +dayjs.extend(relativeTime) +//@ts-ignore +export const fmtUnixTime = (d: BN) => dayjs(d.toNumber() * 1000).fromNow() export function abbreviateAddress(address: PublicKey, size = 5) { const base58 = address.toBase58() diff --git a/utils/splTokens.ts b/utils/splTokens.ts index cb201c9daf..9d6176cd3b 100644 --- a/utils/splTokens.ts +++ b/utils/splTokens.ts @@ -41,8 +41,9 @@ export function getSplTokenMintAddressByUIName( ([_, { name }]) => name === nameToMatch ) + // theoretically impossible case if (!item) { - throw new Error('must be here') + throw new Error('Unable to find SPL token mint address by UI name') } const [, { mint }] = item diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 11a350a53d..3b90fd1ad3 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -144,6 +144,7 @@ export interface WithdrawObligationCollateralAndRedeemReserveLiquidityForm { governedAccount?: GovernedMultiTypeAccount uiAmount: string mintName?: SupportedMintName + destinationLiquidity?: string } export interface RefreshObligationForm { @@ -234,6 +235,13 @@ export enum UXDIntructions { WithdrawInsuranceFromMangoDepository, Grant, Clawback, + CreateAssociatedTokenAccount, + CreateSolendObligationAccount, + InitSolendObligationAccount, + DepositReserveLiquidityAndObligationCollateral, + WithdrawObligationCollateralAndRedeemReserveLiquidity, + RefreshSolendObligation, + RefreshSolendReserve, } export type createParams = [ diff --git a/yarn.lock b/yarn.lock index 82bd3f649d..31496f47ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -96,12 +96,6 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.12.13": - version "7.12.13" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz" - dependencies: - "@babel/types" "^7.12.13" - "@babel/helper-compilation-targets@^7.13.16": version "7.13.16" resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz" @@ -217,7 +211,7 @@ dependencies: "@babel/types" "^7.12.13" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.8.0": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.8.0": version "7.13.0" resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz" @@ -379,7 +373,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.2.0": +"@babel/plugin-syntax-jsx@^7.12.13": version "7.12.13" resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz" dependencies: @@ -434,16 +428,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.5" -"@babel/plugin-transform-react-jsx@^7.12.1": - version "7.13.12" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz" - dependencies: - "@babel/helper-annotate-as-pure" "^7.12.13" - "@babel/helper-module-imports" "^7.13.12" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/plugin-syntax-jsx" "^7.12.13" - "@babel/types" "^7.13.12" - "@babel/runtime-corejs3@^7.10.2": version "7.13.17" resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.13.17.tgz" @@ -685,12 +669,12 @@ "@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" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== "@cspotcode/source-map-support@0.7.0": version "0.7.0" - resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== dependencies: "@cspotcode/source-map-consumer" "0.8.0" @@ -699,13 +683,7 @@ version "1.4.0" resolved "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz" -"@emotion/babel-plugin-jsx-pragmatic@^0.1.5": - version "0.1.5" - resolved "https://registry.npmjs.org/@emotion/babel-plugin-jsx-pragmatic/-/babel-plugin-jsx-pragmatic-0.1.5.tgz" - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - -"@emotion/babel-plugin@^11.2.0", "@emotion/babel-plugin@^11.3.0": +"@emotion/babel-plugin@^11.3.0": version "11.3.0" resolved "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.3.0.tgz" dependencies: @@ -722,15 +700,6 @@ source-map "^0.5.7" stylis "^4.0.3" -"@emotion/babel-preset-css-prop@^11.2.0": - version "11.2.0" - resolved "https://registry.npmjs.org/@emotion/babel-preset-css-prop/-/babel-preset-css-prop-11.2.0.tgz" - dependencies: - "@babel/plugin-transform-react-jsx" "^7.12.1" - "@babel/runtime" "^7.7.2" - "@emotion/babel-plugin" "^11.2.0" - "@emotion/babel-plugin-jsx-pragmatic" "^0.1.5" - "@emotion/cache@^11.1.3": version "11.1.3" resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.1.3.tgz" @@ -1224,27 +1193,6 @@ "@solana/spl-token" "^0.1.8" "@solana/web3.js" "^1.31.0" -"@metaplex/js@^4.10.1": - version "4.10.1" - resolved "https://registry.npmjs.org/@metaplex/js/-/js-4.10.1.tgz" - integrity sha512-f0MBRgZTacj9drM/ANr4gV7Q3NtY+xW6OgP3rfHqrSUCatxHsqeeeAMUwR0bKbiyzdGLwVff9cFGi0muFr9TZA== - dependencies: - "@metaplex-foundation/mpl-auction" "^0.0.2" - "@metaplex-foundation/mpl-core" "^0.0.2" - "@metaplex-foundation/mpl-metaplex" "^0.0.5" - "@metaplex-foundation/mpl-token-metadata" "^0.0.2" - "@metaplex-foundation/mpl-token-vault" "^0.0.2" - "@solana/spl-token" "^0.1.8" - "@solana/web3.js" "^1.30.2" - "@types/bs58" "^4.0.1" - axios "^0.21.4" - bn.js "^5.2.0" - borsh "^0.4.0" - bs58 "^4.0.1" - buffer "^6.0.3" - crypto-hash "^1.3.0" - form-data "^4.0.0" - "@metaplex/js@^4.2.1": version "4.11.7" resolved "https://registry.yarnpkg.com/@metaplex/js/-/js-4.11.7.tgz#48d00ee7a2bf96aed7432ddb68bb227d02385156" @@ -1494,26 +1442,6 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/anchor@^0.19.0": - version "0.19.0" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.19.0.tgz#79f1fbe7c3134860ccbfe458a0e09daf79644885" - integrity sha512-cs0LBmJOrL9eJ8MRNqitnzbpCT5QEzVdJmiIjfNV5YaGn1K9vISR7DtISj3Bdl3KBdLqii4CTw1mpHdi8iXUCg== - dependencies: - "@project-serum/borsh" "^0.2.2" - "@solana/web3.js" "^1.17.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^5.3.1" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - find "^0.3.0" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - "@project-serum/anchor@^0.21.0": version "0.21.0" resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.21.0.tgz#ad5fb33744991ec1900cdb2fd22707c908b12b5f" @@ -2033,10 +1961,10 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solendprotocol/solend-sdk@^0.4.7": - version "0.4.7" - resolved "https://registry.yarnpkg.com/@solendprotocol/solend-sdk/-/solend-sdk-0.4.7.tgz#fd840994f71a4d16be1b1d35bbe8e446d150a7e5" - integrity sha512-jOoZr+B9QFbHNnKZ5Au05lj0+fEu81Eq+5dAxrTyDtG1TOiZgtAlyMO1jV3Uykj5FQrc9V8NA36E26lkHUXthA== +"@solendprotocol/solend-sdk@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@solendprotocol/solend-sdk/-/solend-sdk-0.4.9.tgz#c0e3f24148c4951bf0cfdf16c884bc4ffbb42b7b" + integrity sha512-tVsvE0kap58/eMeeSonv0tEiqlnvYC0x90MXKoGz2D0oyQBx9Eneba5wXDh9FJ3S5S2RFqwXIAnmdOzTLZzV/A== dependencies: "@pythnetwork/client" "^2.5.1" "@solana/buffer-layout" "^3.0.0" @@ -2098,22 +2026,22 @@ "@tsconfig/node10@^1.0.7": version "1.0.8" - resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== "@tsconfig/node12@^1.0.7": version "1.0.9" - resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== "@tsconfig/node14@^1.0.0": version "1.0.1" - resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== "@tsconfig/node16@^1.0.2": version "1.0.2" - resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== "@types/aria-query@^4.2.0": @@ -2546,9 +2474,9 @@ acorn-walk@^7.0.0, acorn-walk@^7.1.1: integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== acorn-walk@^8.1.1: - version "8.1.1" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz" - integrity sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w== + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== acorn@8.5.0: version "8.5.0" @@ -2566,9 +2494,9 @@ acorn@^8.2.4, acorn@^8.6.0: integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== acorn@^8.4.1: - version "8.4.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz" - integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== agent-base@6: version "6.0.2" @@ -2678,7 +2606,7 @@ anymatch@^3.0.3, anymatch@~3.1.1, anymatch@~3.1.2: arg@^4.1.0: version "4.1.3" - resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== arg@^5.0.1: @@ -2874,7 +2802,7 @@ available-typed-arrays@^1.0.5: resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axios@^0.21.1, axios@^0.21.4: +axios@^0.21.1: version "0.21.4" resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== @@ -3852,7 +3780,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@3.0.6: @@ -4023,6 +3951,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +dayjs@^1.10.7: + version "1.10.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" + integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== + debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" @@ -4216,7 +4149,7 @@ diff-sequences@^27.4.0: diff@^4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== diffie-hellman@^5.0.0: @@ -5313,10 +5246,6 @@ handle-thing@^2.0.0: resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== -harmony-reflect@^1.4.6: - version "1.6.2" - resolved "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz" - has-bigints@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz" @@ -5574,12 +5503,6 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -identity-obj-proxy@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz" - dependencies: - harmony-reflect "^1.4.6" - ieee754@^1.1.4, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -6962,7 +6885,7 @@ make-dir@^3.0.0, make-dir@^3.0.2: make-error@^1.1.1: version "1.3.6" - resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== makeerror@1.0.x: @@ -7373,11 +7296,6 @@ modern-normalize@^1.0.0, modern-normalize@^1.1.0: resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7" integrity sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA== -moment@^2.29.1: - version "2.29.1" - resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz" - integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== - ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" @@ -8575,7 +8493,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.0.0, prop-types@^15.5.8, prop-types@^15.7.2: +prop-types@^15.0.0, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz" dependencies: @@ -8583,7 +8501,7 @@ prop-types@^15.0.0, prop-types@^15.5.8, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" -prop-types@^15.6.2: +prop-types@^15.5.8, prop-types@^15.6.2: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8876,7 +8794,7 @@ react-markdown@^7.0.0: react-portal@^4.2.1: version "4.2.1" - resolved "https://registry.npmjs.org/react-portal/-/react-portal-4.2.1.tgz" + resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-4.2.1.tgz#12c1599238c06fb08a9800f3070bea2a3f78b1a6" integrity sha512-fE9kOBagwmTXZ3YGRYb4gcMy+kSA+yLO0xnPankjRlfBv4uCpFXqKPfkpsGQQR15wkZ9EssnvTOl1yMzbkxhPQ== dependencies: prop-types "^15.5.8" @@ -10207,10 +10125,10 @@ trough@^2.0.0: resolved "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz" integrity sha512-FnHq5sTMxC0sk957wHDzRnemFnNBvt/gSY99HzK8F7UP5WAbvP70yX5bd7CjEQkN+TjdxwI7g7lJ6podqrG2/w== -ts-node@^10.4.0: - version "10.4.0" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz" - integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== +ts-node@^10.5.0: + version "10.5.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.5.0.tgz#618bef5854c1fbbedf5e31465cbb224a1d524ef9" + integrity sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw== dependencies: "@cspotcode/source-map-support" "0.7.0" "@tsconfig/node10" "^1.0.7" @@ -10223,6 +10141,7 @@ ts-node@^10.4.0: create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" + v8-compile-cache-lib "^3.0.0" yn "3.1.1" tsconfig-paths@^3.12.0: @@ -10588,6 +10507,11 @@ uuid@^8.3.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8" + integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" @@ -10990,7 +10914,7 @@ yargs@^17.0.1: yn@3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: From dffcc6f72f72f49b96999285e8573577f4f7aa02 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Wed, 23 Feb 2022 22:56:03 +0900 Subject: [PATCH 111/204] smol fix --- components/instructions/tools.tsx | 5 +---- package.json | 2 +- pages/dao/[symbol]/proposal/new.tsx | 6 ++++++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/components/instructions/tools.tsx b/components/instructions/tools.tsx index 59e79d92ab..fc31247f18 100644 --- a/components/instructions/tools.tsx +++ b/components/instructions/tools.tsx @@ -20,8 +20,7 @@ import { ATA_PROGRAM_INSTRUCTIONS } from './programs/associatedTokenAccount' import { SYSTEM_INSTRUCTIONS } from './programs/system' import { VOTE_STAKE_REGISTRY_INSTRUCTIONS } from './programs/voteStakeRegistry' import { MARINADE_INSTRUCTIONS } from './programs/marinade' -import { SOLEND_PROGRAM_INSTRUCTIONS } from './programs/solend' -import { ATA_PROGRAM_INSTRUCTIONS } from './programs/associatedTokenAccount' + /** * Default governance program id instance */ @@ -166,8 +165,6 @@ export const INSTRUCTION_DESCRIPTORS = { ...MANGO_INSTRUCTIONS, ...RAYDIUM_INSTRUCTIONS, ...UXD_PROGRAM_INSTRUCTIONS, - ...SOLEND_PROGRAM_INSTRUCTIONS, - ...ATA_PROGRAM_INSTRUCTIONS, ...MARINADE_INSTRUCTIONS, ...SOLEND_PROGRAM_INSTRUCTIONS, ...ATA_PROGRAM_INSTRUCTIONS, diff --git a/package.json b/package.json index ad9cb39510..06bdd92f45 100644 --- a/package.json +++ b/package.json @@ -99,4 +99,4 @@ "preset": "emotion" } } -} \ No newline at end of file +} diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index 5965ad33c6..68c9ca3e90 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -51,6 +51,12 @@ import MakeChangeReferralFeeParams from './components/instructions/Mango/MakeCha import Mint from './components/instructions/Mint' import SplTokenTransfer from './components/instructions/SplTokenTransfer' import VoteBySwitch from './components/VoteBySwitch' +import CreateObligationAccount from './components/instructions/solend/CreateObligationAccount' +import DepositReserveLiquidityAndObligationCollateral from './components/instructions/solend/DepositReserveLiquidityAndObligationCollateral' +import InitObligationAccount from './components/instructions/solend/InitObligationAccount' +import RefreshObligation from './components/instructions/solend/RefreshObligation' +import RefreshReserve from './components/instructions/solend/RefreshReserve' +import WithdrawObligationCollateralAndRedeemReserveLiquidity from './components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity' const schema = yup.object().shape({ title: yup.string().required('Title is required'), From 60300802a9746a055152669017dc5ec1a88bac31 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 25 Feb 2022 14:58:21 +0900 Subject: [PATCH 112/204] fix typo --- .../WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx index 90213d5c53..af15165698 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx @@ -178,7 +178,7 @@ const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({ /> From c87f6568371024734da9ab73df2846cb56079c53 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 25 Feb 2022 15:09:13 +0900 Subject: [PATCH 113/204] Delete pages/dao/[symbol]/proposal/components/instructions/solend directory was a clone of `Solend` directory that appeared during merge --- .../solend/CreateObligationAccount.tsx | 125 ----------- ...eserveLiquidityAndObligationCollateral.tsx | 179 ---------------- .../solend/InitObligationAccount.tsx | 127 ------------ .../instructions/solend/RefreshObligation.tsx | 147 ------------- .../instructions/solend/RefreshReserve.tsx | 145 ------------- ...ionCollateralAndRedeemReserveLiquidity.tsx | 196 ------------------ 6 files changed, 919 deletions(-) delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/solend/InitObligationAccount.tsx delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/solend/RefreshObligation.tsx delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/solend/RefreshReserve.tsx delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx deleted file mode 100644 index 5f8ce2ed40..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/CreateObligationAccount.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' -import * as yup from 'yup' -import { PublicKey } from '@solana/web3.js' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' -import useRealm from '@hooks/useRealm' -import { createObligationAccount } from '@tools/sdk/solend/createObligationAccount' -import { isFormValid } from '@utils/formValidation' -import { - CreateSolendObligationAccountForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' - -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' - -const CreateObligationAccount = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - - // Hardcoded gate used to be clear about what cluster is supported for now - if (connection.cluster !== 'mainnet') { - return <>This instruction does not support {connection.cluster} - } - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({}) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.governedAccount?.governance?.account || - !wallet?.publicKey - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await createObligationAccount({ - fundingAddress: wallet.publicKey, - walletAddress: form.governedAccount.governance.pubkey, - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, - }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - }) - - return ( - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - ) -} - -export default CreateObligationAccount diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx deleted file mode 100644 index ac6be4ce2d..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/DepositReserveLiquidityAndObligationCollateral.tsx +++ /dev/null @@ -1,179 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import BigNumber from 'bignumber.js' -import * as yup from 'yup' - -import { BN } from '@project-serum/anchor' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' -import Input from '@components/inputs/Input' -import Select from '@components/inputs/Select' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' -import useRealm from '@hooks/useRealm' -import SolendConfiguration from '@tools/sdk/solend/configuration' -import { depositReserveLiquidityAndObligationCollateral } from '@tools/sdk/solend/depositReserveLiquidityAndObligationCollateral' -import { isFormValid } from '@utils/formValidation' -import { - DepositReserveLiquidityAndObligationCollateralForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' -import useWalletStore from 'stores/useWalletStore' -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' - -const DepositReserveLiquidityAndObligationCollateral = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - // Hardcoded gate used to be clear about what cluster is supported for now - if (connection.cluster !== 'mainnet') { - return <>This instruction does not support {connection.cluster} - } - - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [ - form, - setForm, - ] = useState({ - uiAmount: '0', - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.governedAccount?.governance?.account || - !wallet?.publicKey || - !form.mintName - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await depositReserveLiquidityAndObligationCollateral({ - obligationOwner: form.governedAccount.governance.pubkey, - liquidityAmount: new BN( - new BigNumber(form.uiAmount) - .shiftedBy( - SolendConfiguration.getSupportedMintInformation(form.mintName) - .decimals - ) - .toString() - ), - mintName: form.mintName, - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, - }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - uiAmount: yup - .number() - .moreThan(0, 'Amount should be more than 0') - .required('Amount is required'), - }) - - return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - - - - - handleSetForm({ - value: evt.target.value, - propertyName: 'uiAmount', - }) - } - error={formErrors['uiAmount']} - /> - - ) -} - -export default DepositReserveLiquidityAndObligationCollateral diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/InitObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/InitObligationAccount.tsx deleted file mode 100644 index 455cad275b..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/InitObligationAccount.tsx +++ /dev/null @@ -1,127 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import * as yup from 'yup' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' - -import useRealm from '@hooks/useRealm' -import { initObligationAccount } from '@tools/sdk/solend/initObligationAccount' -import { isFormValid } from '@utils/formValidation' -import { - InitSolendObligationAccountForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' - -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' - -const InitObligationAccount = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - - // Hardcoded gate used to be clear about what cluster is supported for now - if (connection.cluster !== 'mainnet') { - return <>This instruction does not support {connection.cluster} - } - - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({}) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.governedAccount?.governance?.account || - !wallet?.publicKey - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await initObligationAccount({ - obligationOwner: form.governedAccount.governance.pubkey, - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, - }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - }) - - return ( - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - ) -} - -export default InitObligationAccount diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshObligation.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshObligation.tsx deleted file mode 100644 index 7e6da08836..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshObligation.tsx +++ /dev/null @@ -1,147 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import * as yup from 'yup' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' -import Select from '@components/inputs/Select' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' -import useRealm from '@hooks/useRealm' -import SolendConfiguration from '@tools/sdk/solend/configuration' -import { refreshObligation } from '@tools/sdk/solend/refreshObligation' -import { isFormValid } from '@utils/formValidation' -import { - RefreshObligationForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' - -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' - -const RefreshObligation = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - - // Hardcoded gate used to be clear about what cluster is supported for now - if (connection.cluster !== 'mainnet') { - return <>This instruction does not support {connection.cluster} - } - - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({}) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.governedAccount?.governance?.account || - !form.mintName || - !wallet?.publicKey - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await refreshObligation({ - obligationOwner: form.governedAccount.governance.pubkey, - mintNames: [form.mintName], - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, - }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - }) - - return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - - - - ) -} - -export default RefreshObligation diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshReserve.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshReserve.tsx deleted file mode 100644 index a90c519d36..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/RefreshReserve.tsx +++ /dev/null @@ -1,145 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import * as yup from 'yup' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' -import Select from '@components/inputs/Select' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' -import useRealm from '@hooks/useRealm' -import SolendConfiguration from '@tools/sdk/solend/configuration' -import { refreshReserve } from '@tools/sdk/solend/refreshReserve' -import { isFormValid } from '@utils/formValidation' -import { - RefreshReserveForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' - -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' - -const RefreshReserve = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - const shouldBeGoverned = index !== 0 && governance - - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({}) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - // Hardcoded gate used to be clear about what cluster is supported for now - if (connection.cluster !== 'mainnet') { - return <>This instruction does not support {connection.cluster} - } - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.mintName || - !form.governedAccount?.governance.account || - !wallet?.publicKey - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await refreshReserve({ - mintName: form.mintName, - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount?.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, - }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - }) - - return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - - - ) -} - -export default RefreshReserve diff --git a/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx deleted file mode 100644 index 90213d5c53..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx +++ /dev/null @@ -1,196 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import Input from '@components/inputs/Input' -import BigNumber from 'bignumber.js' -import * as yup from 'yup' -import { BN } from '@project-serum/anchor' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' -import Select from '@components/inputs/Select' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' -import useRealm from '@hooks/useRealm' -import SolendConfiguration from '@tools/sdk/solend/configuration' -import { withdrawObligationCollateralAndRedeemReserveLiquidity } from '@tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - WithdrawObligationCollateralAndRedeemReserveLiquidityForm, -} from '@utils/uiTypes/proposalCreationTypes' - -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' - -const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - - // Hardcoded gate used to be clear about what cluster is supported for now - if (connection.cluster !== 'mainnet') { - return <>This instruction does not support {connection.cluster} - } - - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [ - form, - setForm, - ] = useState({ - uiAmount: '0', - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.governedAccount?.governance?.account || - !wallet?.publicKey || - !form.mintName - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await withdrawObligationCollateralAndRedeemReserveLiquidity({ - obligationOwner: form.governedAccount.governance.pubkey, - liquidityAmount: new BN( - new BigNumber(form.uiAmount) - .shiftedBy( - SolendConfiguration.getSupportedMintInformation(form.mintName) - .decimals - ) - .toString() - ), - mintName: form.mintName, - ...(form.destinationLiquidity && { - destinationLiquidity: new PublicKey(form.destinationLiquidity), - }), - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, - }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - uiAmount: yup - .number() - .moreThan(0, 'Amount should be more than 0') - .required('Amount is required'), - }) - - return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - - - - - handleSetForm({ - value: evt.target.value, - propertyName: 'uiAmount', - }) - } - error={formErrors['uiAmount']} - /> - - - handleSetForm({ - value: evt.target.value, - propertyName: 'destinationLiquidity', - }) - } - error={formErrors['destinationLiquidity']} - /> - - ) -} - -export default WithdrawObligationCollateralAndRedeemReserveLiquidity From 81f1e4ed9f67311ef7288629328e33bc8eed7387 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 25 Feb 2022 15:18:42 +0900 Subject: [PATCH 114/204] fix deleted file --- ...ionCollateralAndRedeemReserveLiquidity.tsx | 196 ++++++++++++++++++ pages/dao/[symbol]/proposal/new.tsx | 12 +- 2 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx new file mode 100644 index 0000000000..90213d5c53 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx @@ -0,0 +1,196 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useContext, useEffect, useState } from 'react' +import Input from '@components/inputs/Input' +import BigNumber from 'bignumber.js' +import * as yup from 'yup' +import { BN } from '@project-serum/anchor' +import { + Governance, + ProgramAccount, + serializeInstructionToBase64, +} from '@solana/spl-governance' +import { PublicKey } from '@solana/web3.js' +import Select from '@components/inputs/Select' +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' +import useRealm from '@hooks/useRealm' +import SolendConfiguration from '@tools/sdk/solend/configuration' +import { withdrawObligationCollateralAndRedeemReserveLiquidity } from '@tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity' +import { isFormValid } from '@utils/formValidation' +import { + UiInstruction, + WithdrawObligationCollateralAndRedeemReserveLiquidityForm, +} from '@utils/uiTypes/proposalCreationTypes' + +import useWalletStore from 'stores/useWalletStore' + +import { NewProposalContext } from '../../../new' +import GovernedAccountSelect from '../../GovernedAccountSelect' + +const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({ + index, + governance, +}: { + index: number + governance: ProgramAccount | null +}) => { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { realmInfo } = useRealm() + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + const shouldBeGoverned = index !== 0 && governance + const programId: PublicKey | undefined = realmInfo?.programId + const [ + form, + setForm, + ] = useState({ + uiAmount: '0', + }) + const [formErrors, setFormErrors] = useState({}) + const { handleSetInstructions } = useContext(NewProposalContext) + + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + + const validateInstruction = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + + async function getInstruction(): Promise { + const isValid = await validateInstruction() + + if ( + !connection || + !isValid || + !programId || + !form.governedAccount?.governance?.account || + !wallet?.publicKey || + !form.mintName + ) { + return { + serializedInstruction: '', + isValid: false, + governance: form.governedAccount?.governance, + } + } + + const tx = await withdrawObligationCollateralAndRedeemReserveLiquidity({ + obligationOwner: form.governedAccount.governance.pubkey, + liquidityAmount: new BN( + new BigNumber(form.uiAmount) + .shiftedBy( + SolendConfiguration.getSupportedMintInformation(form.mintName) + .decimals + ) + .toString() + ), + mintName: form.mintName, + ...(form.destinationLiquidity && { + destinationLiquidity: new PublicKey(form.destinationLiquidity), + }), + }) + + return { + serializedInstruction: serializeInstructionToBase64(tx), + isValid: true, + governance: form.governedAccount.governance, + } + } + + useEffect(() => { + handleSetForm({ + propertyName: 'programId', + value: programId?.toString(), + }) + }, [programId]) + + useEffect(() => { + handleSetInstructions( + { + governedAccount: form.governedAccount?.governance, + getInstruction, + }, + index + ) + }, [form]) + + const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), + uiAmount: yup + .number() + .moreThan(0, 'Amount should be more than 0') + .required('Amount is required'), + }) + + return ( + <> + { + handleSetForm({ value, propertyName: 'governedAccount' }) + }} + value={form.governedAccount} + error={formErrors['governedAccount']} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + /> + + + + + handleSetForm({ + value: evt.target.value, + propertyName: 'uiAmount', + }) + } + error={formErrors['uiAmount']} + /> + + + handleSetForm({ + value: evt.target.value, + propertyName: 'destinationLiquidity', + }) + } + error={formErrors['destinationLiquidity']} + /> + + ) +} + +export default WithdrawObligationCollateralAndRedeemReserveLiquidity diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index 68c9ca3e90..fcda094bf3 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -51,12 +51,12 @@ import MakeChangeReferralFeeParams from './components/instructions/Mango/MakeCha import Mint from './components/instructions/Mint' import SplTokenTransfer from './components/instructions/SplTokenTransfer' import VoteBySwitch from './components/VoteBySwitch' -import CreateObligationAccount from './components/instructions/solend/CreateObligationAccount' -import DepositReserveLiquidityAndObligationCollateral from './components/instructions/solend/DepositReserveLiquidityAndObligationCollateral' -import InitObligationAccount from './components/instructions/solend/InitObligationAccount' -import RefreshObligation from './components/instructions/solend/RefreshObligation' -import RefreshReserve from './components/instructions/solend/RefreshReserve' -import WithdrawObligationCollateralAndRedeemReserveLiquidity from './components/instructions/solend/WithdrawObligationCollateralAndRedeemReserveLiquidity' +import CreateObligationAccount from './components/instructions/Solend/CreateObligationAccount' +import DepositReserveLiquidityAndObligationCollateral from './components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral' +import InitObligationAccount from './components/instructions/Solend/InitObligationAccount' +import RefreshObligation from './components/instructions/Solend/RefreshObligation' +import RefreshReserve from './components/instructions/Solend/RefreshReserve' +import WithdrawObligationCollateralAndRedeemReserveLiquidity from './components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity' const schema = yup.object().shape({ title: yup.string().required('Title is required'), From 4a2b0863be972e3b80f1691fb802a92cbbb36626 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 11 Mar 2022 22:20:11 +0900 Subject: [PATCH 115/204] fix missing itx in enum --- utils/uiTypes/proposalCreationTypes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 769a6497a9..95d6596b67 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -185,6 +185,7 @@ export enum Instructions { MangoMakeChangeMaxAccounts, MangoChangeReferralFeeParams, CreateAssociatedTokenAccount, + DepositIntoVolt, CreateSolendObligationAccount, InitSolendObligationAccount, DepositReserveLiquidityAndObligationCollateral, From d79f309d9c6b75ebb930ec8f2047e36345fe2a76 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Fri, 11 Mar 2022 22:28:13 +0900 Subject: [PATCH 116/204] update lock --- yarn.lock | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/yarn.lock b/yarn.lock index 1b59e6248a..2e9e60b971 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1593,6 +1593,26 @@ resolved "https://registry.yarnpkg.com/@project-serum/anchor-cli/-/anchor-cli-0.18.2.tgz#748ce259fa693fb3589a96aa9f82bd373c078948" integrity sha512-zZvLSa0DvsjsKSK8cU7ENqPB1tQ8WCT6dTY2IaJjWdG0UvXUTri8phnYyF5+Zti+frTdKyggOtI+i1QApOWhRQ== +"@project-serum/anchor@0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.20.0.tgz#547f5c0ff7e66809fa7118b2e3abd8087b5ec519" + integrity sha512-p1KOiqGBIbNsopMrSVoPwgxR1iPffsdjMNCOysahTPL9whX2CLX9HQCdopHjYaGl7+SdHRuXml6Wahk/wUmC8g== + dependencies: + "@project-serum/borsh" "^0.2.2" + "@solana/web3.js" "^1.17.0" + base64-js "^1.5.1" + bn.js "^5.1.2" + bs58 "^4.0.1" + buffer-layout "^1.2.2" + camelcase "^5.3.1" + crypto-hash "^1.3.0" + eventemitter3 "^4.0.7" + find "^0.3.0" + js-sha256 "^0.9.0" + pako "^2.0.3" + snake-case "^3.0.4" + toml "^3.0.0" + "@project-serum/anchor@0.20.1", "@project-serum/anchor@^0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.20.1.tgz#0937807e807e8332aa708cfef4bcb6cbb88b4129" @@ -2220,6 +2240,26 @@ "@solana/wallet-adapter-base" "^0.9.1" "@solana/web3.js" "^1.20.0" +"@solana/web3.js@1.21.0": + version "1.21.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.21.0.tgz#4f98edea38d4cb3ae4d2ea49a050b0ec09508023" + integrity sha512-x1NXlF92tEjxuTxS0u4n9JV17UKk0Dn2L+qSWGvKOb4iWhzApDj6wicJsrGdSbGdxnZ7eciQ/SNn3zB4ydUllA== + dependencies: + "@babel/runtime" "^7.12.5" + "@solana/buffer-layout" "^3.0.0" + bn.js "^5.0.0" + borsh "^0.4.0" + bs58 "^4.0.1" + buffer "6.0.1" + crypto-hash "^1.2.2" + jayson "^3.4.4" + js-sha3 "^0.8.0" + node-fetch "^2.6.1" + rpc-websockets "^7.4.2" + secp256k1 "^4.0.2" + superstruct "^0.14.2" + tweetnacl "^1.0.0" + "@solana/web3.js@1.31.0", "@solana/web3.js@^1.12.0", "@solana/web3.js@^1.31.0": version "1.31.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.31.0.tgz#7a313d4c1a90b77f27ddbfe845a10d6883e06452" @@ -2862,6 +2902,20 @@ toformat "^2.0.0" tslib "^2.3.1" +"@uxdprotocol/uxd-client@4.10.0": + version "4.10.0" + resolved "https://npm.pkg.github.com/download/@uxdprotocol/uxd-client/4.10.0/9d0a6f2286b67ca56fad70c4269cf96dd758525ffbfb7768f6b952ec9e9794f3#8b3fab7c620a662890dd00a311af3d0b4cbe3434" + integrity sha512-GKf/4fUkdbp67mUTB2kVysxL5EnYuwlkzmAV8YstLRdzPSUZfegwN27doPL+u4eXB3PAcidYzJBpvkg2VLdgwQ== + dependencies: + "@blockworks-foundation/mango-client" "3.2.15" + "@project-serum/anchor" "0.20.0" + camelcase "^5.3.1" + +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -3931,6 +3985,11 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + ci-info@^3.2.0: version "3.3.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz" @@ -5589,6 +5648,15 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^8.1: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -6464,6 +6532,13 @@ is-callable@^1.1.4, is-callable@^1.2.4: resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + is-cidr@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-4.0.2.tgz#94c7585e4c6c77ceabf920f8cde51b8c0fda8814" @@ -8954,6 +9029,14 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -8995,6 +9078,11 @@ original@^1.0.0: dependencies: url-parse "^1.4.3" +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" @@ -9139,6 +9227,25 @@ pascalcase@^0.1.1: resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= +patch-package@^6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148" + integrity sha512-S0vh/ZEafZ17hbhgqdnpunKDfzHQibQizx9g8yEf5dcVk3KOflOfdufRXQX8CSEkyOQwuM/bNz1GwKvFj54kaQ== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^2.4.2" + cross-spawn "^6.0.5" + find-yarn-workspace-root "^2.0.0" + fs-extra "^7.0.1" + is-ci "^2.0.0" + klaw-sync "^6.0.0" + minimist "^1.2.0" + open "^7.4.2" + rimraf "^2.6.3" + semver "^5.6.0" + slash "^2.0.0" + tmp "^0.0.33" + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz" @@ -9651,6 +9758,11 @@ postcss@^8.2.12: picocolors "^1.0.0" source-map-js "^1.0.1" +postinstall-postinstall@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" + integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" From 1e74761a72a79f7552ed6717befa9c80b5240234 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Thu, 24 Mar 2022 23:21:37 +0900 Subject: [PATCH 117/204] Refactor instructions part1 (#50) * refactor instructions form * remove mango itxs * smol fixes * update from comments --- hooks/useGovernanceAssets.ts | 76 ++---- hooks/useInstructionFormBuilder.ts | 111 ++++++++ package.json | 4 +- .../proposal/components/SelectOptionList.tsx | 20 ++ .../CreateAssociatedTokenAccount.tsx | 196 +++++--------- .../Mango/MakeChangeMaxAccounts.tsx | 155 ----------- .../Mango/MakeChangeReferralFeeParams.tsx | 195 -------------- .../components/instructions/ProposalForm.tsx | 68 +++++ .../Raydium/AddLiquidityToPool.tsx | 162 ++++++++++++ .../Raydium/RemoveLiquidityFromPool.tsx | 122 +++++++++ .../instructions/SelectedInstruction.tsx | 153 +++++++++++ .../instructions/SetProgramAuthority.tsx | 4 +- .../Solend/CreateObligationAccount.tsx | 139 +++------- ...eserveLiquidityAndObligationCollateral.tsx | 181 ++++--------- .../Solend/InitObligationAccount.tsx | 135 ++-------- .../instructions/Solend/RefreshObligation.tsx | 169 ++++-------- .../instructions/Solend/RefreshReserve.tsx | 166 +++--------- ...ionCollateralAndRedeemReserveLiquidity.tsx | 189 ++++---------- .../instructions/raydium/AddLiquidity.tsx | 243 ------------------ .../instructions/raydium/RemoveLiquidity.tsx | 210 --------------- pages/dao/[symbol]/proposal/new.tsx | 166 ++---------- .../raydium/createAddLiquidityInstruction.ts | 4 +- .../createRemoveLiquidityInstruction.ts | 6 +- tools/sdk/raydium/helpers.ts | 49 ++-- tools/sdk/solend/utils.ts | 2 + tools/sdk/units.ts | 10 + utils/uiTypes/proposalCreationTypes.ts | 128 ++++----- yarn.lock | 23 +- 28 files changed, 1088 insertions(+), 1998 deletions(-) create mode 100644 hooks/useInstructionFormBuilder.ts create mode 100644 pages/dao/[symbol]/proposal/components/SelectOptionList.tsx delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/Mango/MakeChangeMaxAccounts.tsx delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/Mango/MakeChangeReferralFeeParams.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Raydium/AddLiquidityToPool.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Raydium/RemoveLiquidityFromPool.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index 385ec01fc6..e3f830ce5b 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -138,77 +138,73 @@ export default function useGovernanceAssets() { const availableInstructions = [ { - id: Instructions.CreateAssociatedTokenAccount, - name: 'Create Associated Token Account', - isVisible: canUseAnyInstruction, - }, - { - id: Instructions.CreateSolendObligationAccount, + id: Instructions.SolendCreateObligationAccount, name: 'Solend: Create Obligation Account', isVisible: canUseAnyInstruction, }, { - id: Instructions.InitSolendObligationAccount, + id: Instructions.SolendInitObligationAccount, name: 'Solend: Init Obligation Account', isVisible: canUseAnyInstruction, }, { - id: Instructions.DepositReserveLiquidityAndObligationCollateral, + id: Instructions.SolendDepositReserveLiquidityAndObligationCollateral, name: 'Solend: Deposit Funds', isVisible: canUseAnyInstruction, }, { - id: Instructions.RefreshSolendReserve, + id: Instructions.SolendRefreshReserve, name: 'Solend: Refresh Reserve', isVisible: canUseAnyInstruction, }, { - id: Instructions.RefreshSolendObligation, + id: Instructions.SolendRefreshObligation, name: 'Solend: Refresh Obligation', isVisible: canUseAnyInstruction, }, { - id: Instructions.WithdrawObligationCollateralAndRedeemReserveLiquidity, + id: + Instructions.SolendWithdrawObligationCollateralAndRedeemReserveLiquidity, name: 'Solend: Withdraw Funds', isVisible: canUseAnyInstruction, }, { - id: Instructions.InitializeController, + id: Instructions.UXDInitializeController, name: 'UXD: Initialize Controller', isVisible: canUseUxdInstructions, }, { - id: Instructions.SetRedeemableGlobalSupplyCap, + id: Instructions.UXDSetRedeemableGlobalSupplyCap, name: 'UXD: Set Redeemable Global Supply Cap', isVisible: canUseUxdInstructions, }, { - id: Instructions.SetMangoDepositoriesRedeemableSoftCap, + id: Instructions.UXDSetMangoDepositoriesRedeemableSoftCap, name: 'UXD: Set Mango Depositories Redeemable Supply Soft Cap', isVisible: canUseUxdInstructions, }, { - id: Instructions.RegisterMangoDepository, + id: Instructions.UXDRegisterMangoDepository, name: 'UXD: Register Mango Depository', isVisible: canUseUxdInstructions, }, { - id: Instructions.DepositInsuranceToMangoDepository, + id: Instructions.UXDDepositInsuranceToMangoDepository, name: 'UXD: Deposit Insurance To Mango Depository', isVisible: canUseUxdInstructions, }, { - id: Instructions.WithdrawInsuranceFromMangoDepository, + id: Instructions.UXDWithdrawInsuranceFromMangoDepository, name: 'UXD: Withdraw Insurance From Mango Depository', isVisible: canUseUxdInstructions, }, { - id: Instructions.AddLiquidityRaydium, + id: Instructions.RaydiumAddLiquidity, name: 'Raydium: Add To Liquidity Pool', isVisible: canUseAnyInstruction, }, { - id: Instructions.RemoveLiquidityRaydium, + id: Instructions.RaydiumRemoveLiquidity, name: 'Raydium: Remove From Liquidity Pool', isVisible: canUseAnyInstruction, }, @@ -237,40 +233,10 @@ export default function useGovernanceAssets() { isVisible: canUseAnyInstruction, }, { - id: Instructions.DepositIntoVolt, + id: Instructions.FriktionDepositIntoVolt, name: 'Friktion: Deposit into Volt', isVisible: canUseAnyInstruction, }, - { - id: Instructions.CreateSolendObligationAccount, - name: 'Solend: Create Obligation Account', - isVisible: canUseAnyInstruction, - }, - { - id: Instructions.InitSolendObligationAccount, - name: 'Solend: Init Obligation Account', - isVisible: canUseAnyInstruction, - }, - { - id: Instructions.DepositReserveLiquidityAndObligationCollateral, - name: 'Solend: Deposit Funds', - isVisible: canUseAnyInstruction, - }, - { - id: Instructions.RefreshSolendReserve, - name: 'Solend: Refresh Reserve', - isVisible: canUseAnyInstruction, - }, - { - id: Instructions.RefreshSolendObligation, - name: 'Solend: Refresh Obligation', - isVisible: canUseAnyInstruction, - }, - { - id: Instructions.WithdrawObligationCollateralAndRedeemReserveLiquidity, - name: 'Solend: Withdraw Funds', - isVisible: canUseAnyInstruction, - }, { id: Instructions.ProgramUpgrade, name: 'Upgrade Program', @@ -291,16 +257,6 @@ export default function useGovernanceAssets() { name: 'Execute Custom Instruction', isVisible: canUseAnyInstruction, }, - { - id: Instructions.MangoMakeChangeMaxAccounts, - name: 'Mango - change max accounts', - isVisible: canUseProgramUpgradeInstruction && symbol === 'MNGO', - }, - { - id: Instructions.MangoChangeReferralFeeParams, - name: 'Mango - change referral fee params', - isVisible: canUseProgramUpgradeInstruction && symbol === 'MNGO', - }, { id: Instructions.None, name: 'None', diff --git a/hooks/useInstructionFormBuilder.ts b/hooks/useInstructionFormBuilder.ts new file mode 100644 index 0000000000..1407b2f44a --- /dev/null +++ b/hooks/useInstructionFormBuilder.ts @@ -0,0 +1,111 @@ +import { useContext, useEffect, useState } from 'react' +import * as yup from 'yup' +import { serializeInstructionToBase64 } from '@solana/spl-governance' +import { TransactionInstruction } from '@solana/web3.js' +import { debounce } from '@utils/debounce' +import { isFormValid } from '@utils/formValidation' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { UiInstruction } from '@utils/uiTypes/proposalCreationTypes' + +import { NewProposalContext } from 'pages/dao/[symbol]/proposal/new' +import useWalletStore from 'stores/useWalletStore' + +function useInstructionFormBuilder< + T extends { + governedAccount?: GovernedMultiTypeAccount + } +>({ + index, + initialFormValues, + schema, + buildInstruction, +}: { + index?: number + initialFormValues: T + schema: yup.ObjectSchema< + { + [key in keyof T]: yup.AnySchema + } + > + buildInstruction?: () => Promise +}) { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + const { handleSetInstructions } = useContext(NewProposalContext) + + const [form, setForm] = useState(initialFormValues) + const [formErrors, setFormErrors] = useState({}) + + const handleSetForm = ({ propertyName, value }) => { + setFormErrors({}) + setForm({ ...form, [propertyName]: value }) + } + + const validateForm = async (): Promise => { + const { isValid, validationErrors } = await isFormValid(schema, form) + setFormErrors(validationErrors) + return isValid + } + + const getInstruction = async (): Promise => { + if ( + !wallet?.publicKey || + !form.governedAccount?.governance?.account || + !buildInstruction || + !(await validateForm()) + ) { + return { + serializedInstruction: '', + isValid: false, + governance: form.governedAccount?.governance, + } + } + + try { + return { + serializedInstruction: serializeInstructionToBase64( + await buildInstruction() + ), + isValid: true, + governance: form.governedAccount?.governance, + } + } catch (e) { + console.error(e) + return { + serializedInstruction: '', + isValid: false, + governance: form.governedAccount?.governance, + } + } + } + + useEffect(() => { + handleSetForm({ + propertyName: 'governedAccount', + value: initialFormValues.governedAccount, + }) + }, [JSON.stringify(initialFormValues.governedAccount)]) + + useEffect(() => { + console.debug('form', form) + debounce.debounceFcn(async () => { + await validateForm() + }) + handleSetInstructions( + { governedAccount: form.governedAccount?.governance, getInstruction }, + index + ) + }, [form]) + + return { + form, + setForm, + wallet, + connection, + formErrors, + handleSetForm, + validateForm, + } +} + +export default useInstructionFormBuilder diff --git a/package.json b/package.json index 4102afa844..11a21e098c 100644 --- a/package.json +++ b/package.json @@ -38,14 +38,14 @@ "@project-serum/anchor": "^0.20.1", "@project-serum/common": "^0.0.1-beta.3", "@project-serum/sol-wallet-adapter": "^0.2.0", - "@raydium-io/raydium-sdk": "^1.0.1-beta.26", + "@raydium-io/raydium-sdk": "^1.0.1-beta.34", "@solana/spl-governance": "^0.0.28", "@solana/spl-token": "^0.1.3", "@solana/spl-token-registry": "^0.2.1733", "@solana/wallet-adapter-base": "^0.7.1", "@solana/wallet-adapter-phantom": "^0.7.0", "@solana/wallet-adapter-sollet": "^0.8.0", - "@solana/web3.js": "^1.31.0", + "@solana/web3.js": "^1.34.0", "@solendprotocol/solend-sdk": "^0.4.9", "@tippyjs/react": "^4.2.5", "@uxdprotocol/uxd-client": "4.10.0", diff --git a/pages/dao/[symbol]/proposal/components/SelectOptionList.tsx b/pages/dao/[symbol]/proposal/components/SelectOptionList.tsx new file mode 100644 index 0000000000..c8fcefa2e0 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/SelectOptionList.tsx @@ -0,0 +1,20 @@ +import Select from '@components/inputs/Select' + +type Props = { + list: string[] | number[] +} + +const SelectOptionList = ({ list }: Props) => { + if (!list?.length) return null + return ( + <> + {list.map((elt) => ( + + {elt} + + ))} + + ) +} + +export default SelectOptionList diff --git a/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx index 7ae6fb92f5..8409a6ba20 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx @@ -1,153 +1,75 @@ -import React, { useContext, useEffect, useState } from 'react' +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React from 'react' import * as yup from 'yup' - -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' import Select from '@components/inputs/Select' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' -import useRealm from '@hooks/useRealm' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import { createAssociatedTokenAccount } from '@utils/associated' -import { isFormValid } from '@utils/formValidation' import { getSplTokenMintAddressByUIName, SPL_TOKENS } from '@utils/splTokens' -import { - CreateAssociatedTokenAccountForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' - -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../new' -import GovernedAccountSelect from '../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { CreateAssociatedTokenAccountForm } from '@utils/uiTypes/proposalCreationTypes' const CreateAssociatedTokenAccount = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount?: GovernedMultiTypeAccount }) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({}) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.governedAccount?.governance?.account || - !form.splTokenMintUIName || - !wallet?.publicKey - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const [tx] = await createAssociatedTokenAccount( - // fundingAddress - wallet.publicKey, - - // walletAddress - form.governedAccount.governance.pubkey, - - // splTokenMintAddress - getSplTokenMintAddressByUIName(form.splTokenMintUIName) - ) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, - }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - splTokenMintUIName: yup.string().required('SPL Token Mint is required'), + const { + wallet, + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + splTokenMintUIName: yup.string().required('SPL Token Mint is required'), + }), + buildInstruction: async function () { + if (!form.splTokenMintUIName || !wallet?.publicKey) + throw new Error('invalid form, missing splTokenMintUiName field') + + const [tx] = await createAssociatedTokenAccount( + // fundingAddress + wallet.publicKey, + + // walletAddress + governedAccount!.governance.pubkey, + + // splTokenMintAddress + getSplTokenMintAddressByUIName(form.splTokenMintUIName) + ) + return tx + }, }) return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - - - + ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/Mango/MakeChangeMaxAccounts.tsx b/pages/dao/[symbol]/proposal/components/instructions/Mango/MakeChangeMaxAccounts.tsx deleted file mode 100644 index 2557d5e8e8..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/Mango/MakeChangeMaxAccounts.tsx +++ /dev/null @@ -1,155 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' -import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - MangoMakeChangeMaxAccountsForm, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' -import { Governance, GovernanceAccountType } from '@solana/spl-governance' -import { ProgramAccount } from '@solana/spl-governance' -import useWalletStore from 'stores/useWalletStore' -import { serializeInstructionToBase64 } from '@solana/spl-governance' -import Input from '@components/inputs/Input' -import GovernedAccountSelect from '../../GovernedAccountSelect' -import { GovernedMultiTypeAccount } from '@utils/tokens' -import { - BN, - makeChangeMaxMangoAccountsInstruction, -} from '@blockworks-foundation/mango-client' - -const MakeChangeMaxAccounts = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { getGovernancesByAccountTypes } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountTypes([ - GovernanceAccountType.ProgramGovernanceV1, - GovernanceAccountType.ProgramGovernanceV2, - ]).map((x) => { - return { - governance: x, - } - }) - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - programId: programId?.toString(), - mangoGroupKey: undefined, - maxMangoAccounts: 1, - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - async function getInstruction(): Promise { - const isValid = await validateInstruction() - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - wallet?.publicKey - ) { - //Mango instruction call and serialize - const setMaxMangoAccountsInstr = makeChangeMaxMangoAccountsInstruction( - form.governedAccount.governance.account.governedAccount, - new PublicKey(form.mangoGroupKey!), - form.governedAccount.governance.pubkey, - new BN(form.maxMangoAccounts) - ) - - serializedInstruction = serializeInstructionToBase64( - setMaxMangoAccountsInstr - ) - } - const obj: UiInstruction = { - serializedInstruction: serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - const schema = yup.object().shape({ - bufferAddress: yup.number(), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), - }) - - return ( - <> - {/* if you need more fields add theme to interface MangoMakeChangeMaxAccountsForm - then you can add inputs in here */} - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - - handleSetForm({ - value: evt.target.value, - propertyName: 'mangoGroupKey', - }) - } - error={formErrors['mangoGroupKey']} - /> - - handleSetForm({ - value: evt.target.value, - propertyName: 'maxMangoAccounts', - }) - } - error={formErrors['maxMangoAccounts']} - /> - - ) -} - -export default MakeChangeMaxAccounts diff --git a/pages/dao/[symbol]/proposal/components/instructions/Mango/MakeChangeReferralFeeParams.tsx b/pages/dao/[symbol]/proposal/components/instructions/Mango/MakeChangeReferralFeeParams.tsx deleted file mode 100644 index 57093b1942..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/Mango/MakeChangeReferralFeeParams.tsx +++ /dev/null @@ -1,195 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' -import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - MangoMakeChangeReferralFeeParams, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' -import { Governance, GovernanceAccountType } from '@solana/spl-governance' -import { ProgramAccount } from '@solana/spl-governance' -import useWalletStore from 'stores/useWalletStore' -import { serializeInstructionToBase64 } from '@solana/spl-governance' -import Input from '@components/inputs/Input' -import GovernedAccountSelect from '../../GovernedAccountSelect' -import { GovernedMultiTypeAccount, tryGetMint } from '@utils/tokens' -import { makeChangeReferralFeeParamsInstruction } from '@blockworks-foundation/mango-client' -import { BN } from '@project-serum/anchor' -import { MANGO_MINT } from 'Strategies/protocols/mango/tools' -import { parseMintNaturalAmountFromDecimal } from '@tools/sdk/units' - -const MakeChangeReferralFeeParams = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { getGovernancesByAccountTypes } = useGovernanceAssets() - const connection = useWalletStore((s) => s.connection) - const governedProgramAccounts = getGovernancesByAccountTypes([ - GovernanceAccountType.ProgramGovernanceV1, - GovernanceAccountType.ProgramGovernanceV2, - ]).map((x) => { - return { - governance: x, - } - }) - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - programId: programId?.toString(), - mangoGroupKey: undefined, - refSurchargeCentibps: 0, - refShareCentibps: 0, - refMngoRequired: 0, - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - async function getInstruction(): Promise { - const isValid = await validateInstruction() - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - wallet?.publicKey - ) { - //Mango instruction call and serialize - const mint = await tryGetMint( - connection.current, - new PublicKey(MANGO_MINT) - ) - const refMngoRequiredMintAmount = parseMintNaturalAmountFromDecimal( - form.refMngoRequired!, - mint!.account.decimals - ) - const setMaxMangoAccountsInstr = makeChangeReferralFeeParamsInstruction( - form.governedAccount.governance.account.governedAccount, - new PublicKey(form.mangoGroupKey!), - form.governedAccount.governance.pubkey, - new BN(form.refSurchargeCentibps), - new BN(form.refShareCentibps), - new BN(refMngoRequiredMintAmount) - ) - - serializedInstruction = serializeInstructionToBase64( - setMaxMangoAccountsInstr - ) - } - const obj: UiInstruction = { - serializedInstruction: serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), - mangoGroupKey: yup.string().required(), - refShareCentibps: yup.number().required(), - refMngoRequired: yup.number().required(), - refSurchargeCentibps: yup.number().required(), - }) - - return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - - handleSetForm({ - value: evt.target.value, - propertyName: 'mangoGroupKey', - }) - } - error={formErrors['mangoGroupKey']} - /> - - handleSetForm({ - value: evt.target.value, - propertyName: 'refSurchargeCentibps', - }) - } - error={formErrors['refSurchargeCentibps']} - /> - - handleSetForm({ - value: evt.target.value, - propertyName: 'refShareCentibps', - }) - } - error={formErrors['refShareCentibps']} - /> - - handleSetForm({ - value: evt.target.value, - propertyName: 'refMngoRequired', - }) - } - error={formErrors['refMngoRequired']} - /> - - ) -} - -export default MakeChangeReferralFeeParams diff --git a/pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx b/pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx new file mode 100644 index 0000000000..3ea1d8a747 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx @@ -0,0 +1,68 @@ +import { useState } from 'react' +import { Governance, ProgramAccount } from '@solana/spl-governance' + +import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' +import { GovernedMultiTypeAccount } from '@utils/tokens' + +import { Instructions } from '@utils/uiTypes/proposalCreationTypes' +import GovernedAccountSelect from '../GovernedAccountSelect' +import SelectedInstruction from './SelectedInstruction' + +const ProposalForm = ({ + index, + governance, + itxType, +}: { + index: number + governance: ProgramAccount | null + itxType: number +}) => { + const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() + + const shouldBeGoverned = index !== 0 && governance + + const [governedAccount, setGovernanceAccount] = useState< + GovernedMultiTypeAccount | undefined + >() + + return ( + <> + {![ + Instructions.Transfer, + Instructions.Mint, + Instructions.ProgramUpgrade, + Instructions.SetProgramAuthority, + Instructions.Base64, + Instructions.Clawback, + Instructions.Grant, + Instructions.None, + Instructions.FriktionDepositIntoVolt, + Instructions.UXDInitializeController, + Instructions.UXDSetRedeemableGlobalSupplyCap, + Instructions.UXDSetMangoDepositoriesRedeemableSoftCap, + Instructions.UXDRegisterMangoDepository, + Instructions.UXDDepositInsuranceToMangoDepository, + Instructions.UXDWithdrawInsuranceFromMangoDepository, + ].includes(itxType) && ( + { + setGovernanceAccount(value) + }} + value={governedAccount} + shouldBeGoverned={shouldBeGoverned} + governance={governance} + /> + )} + + + ) +} + +export default ProposalForm diff --git a/pages/dao/[symbol]/proposal/components/instructions/Raydium/AddLiquidityToPool.tsx b/pages/dao/[symbol]/proposal/components/instructions/Raydium/AddLiquidityToPool.tsx new file mode 100644 index 0000000000..3f8b34abc2 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Raydium/AddLiquidityToPool.tsx @@ -0,0 +1,162 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useEffect } from 'react' +import * as yup from 'yup' +import Input from '@components/inputs/Input' +import Select from '@components/inputs/Select' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' + +import { createAddLiquidityInstruction } from '@tools/sdk/raydium/createAddLiquidityInstruction' +import { + getAmountOut, + getLiquidityPoolKeysByLabel, +} from '@tools/sdk/raydium/helpers' +import { liquidityPoolKeysList } from '@tools/sdk/raydium/poolKeys' +import { debounce } from '@utils/debounce' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { AddLiquidityRaydiumForm } from '@utils/uiTypes/proposalCreationTypes' +import SelectOptionList from '../../SelectOptionList' +import { uiAmountToNativeBN } from '@tools/sdk/units' + +const SLIPPAGE_OPTIONS = [0.5, 1, 2] +const FIXED_SIDE_LIST = ['base', 'quote'] + +const RaydiumAddLiquidityToPool = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const { + form, + connection, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + fixedSide: 'base', + slippage: 0.5, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + liquidityPool: yup.string().required('Liquidity Pool is required'), + baseAmountIn: yup + .number() + .moreThan(0, 'Amount for Base token should be more than 0') + .required('Amount for Base token is required'), + quoteAmountIn: yup + .number() + .moreThan(0, 'Amount for Quote token should be more than 0') + .required('Amount for Quote token is required'), + fixedSide: yup + .string() + .equals(['base', 'quote']) + .required('Fixed Side is required'), + slippage: yup.number().required('Slippage value is required'), + }), + buildInstruction: async function () { + if (!form.liquidityPool || !form.baseAmountIn || !form.quoteAmountIn) + throw new Error('missing field in instruction form') + const poolKeys = getLiquidityPoolKeysByLabel(form.liquidityPool) + const [base, quote] = await Promise.all([ + connection.current.getTokenSupply(poolKeys.baseMint), + connection.current.getTokenSupply(poolKeys.quoteMint), + ]) + return createAddLiquidityInstruction( + poolKeys, + uiAmountToNativeBN(form.baseAmountIn, base.value.decimals), + uiAmountToNativeBN(form.quoteAmountIn, quote.value.decimals), + form.fixedSide, + form.governedAccount!.governance.pubkey + ) + }, + }) + + useEffect(() => { + debounce.debounceFcn(async () => { + if (!form.baseAmountIn || !form.liquidityPool) return + + handleSetForm({ + value: await getAmountOut( + form.liquidityPool, + form.baseAmountIn, + connection.current, + form.slippage + ), + propertyName: 'quoteAmountIn', + }) + }) + }, [form.baseAmountIn, form.slippage]) + + return ( + <> + + + {form.liquidityPool && ( + <> + + handleSetForm({ + value: evt.target.value, + propertyName: 'baseAmountIn', + }) + } + error={formErrors['baseAmountIn']} + /> + + + + + + + )} + + ) +} + +export default RaydiumAddLiquidityToPool diff --git a/pages/dao/[symbol]/proposal/components/instructions/Raydium/RemoveLiquidityFromPool.tsx b/pages/dao/[symbol]/proposal/components/instructions/Raydium/RemoveLiquidityFromPool.tsx new file mode 100644 index 0000000000..e4fd2553ba --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Raydium/RemoveLiquidityFromPool.tsx @@ -0,0 +1,122 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useEffect, useState } from 'react' +import * as yup from 'yup' +import { jsonInfo2PoolKeys } from '@raydium-io/raydium-sdk' +import { PublicKey } from '@solana/web3.js' +import Input from '@components/inputs/Input' +import Select from '@components/inputs/Select' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import { createRemoveLiquidityInstruction } from '@tools/sdk/raydium/createRemoveLiquidityInstruction' +import { fetchLiquidityPoolData } from '@tools/sdk/raydium/helpers' +import { liquidityPoolKeysList } from '@tools/sdk/raydium/poolKeys' +import { notify } from '@utils/notifications' +import { RemoveLiquidityRaydiumForm } from '@utils/uiTypes/proposalCreationTypes' + +import SelectOptionList from '../../SelectOptionList' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { uiAmountToNativeBN } from '@tools/sdk/units' + +const POOL_KEYS_OPTIONS = Object.keys(liquidityPoolKeysList) + +const RaydiumRemoveLiquidityFromPool = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const [lpMintInfo, setLpMintInfo] = useState<{ + balance: number + decimals: number + } | null>(null) + + const { + form, + connection, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + liquidityPool: '', + amountIn: 0, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + liquidityPool: yup.string().required('Liquidity Pool is required'), + amountIn: yup + .number() + .moreThan(0, 'Amount for LP token should be more than 0') + .required('Amount for LP token is required'), + }), + buildInstruction: async function () { + if (!form.liquidityPool || !lpMintInfo) { + throw new Error('missing field in instruction form') + } + + return createRemoveLiquidityInstruction( + new PublicKey(governedAccount!.governance.pubkey), + jsonInfo2PoolKeys(liquidityPoolKeysList[form.liquidityPool]), + uiAmountToNativeBN(form.amountIn, lpMintInfo.decimals) + ) + }, + }) + + useEffect(() => { + async function fetchLpMintInfo() { + try { + const { maxBalance, decimals } = await fetchLiquidityPoolData({ + governanceKey: governedAccount?.governance.pubkey, + lp: form.liquidityPool, + connection: connection.current, + }) + setLpMintInfo({ balance: maxBalance, decimals }) + } catch (e) { + notify({ + type: 'error', + message: 'Could not fetch LP Account', + description: `${form.liquidityPool} LP Token Account could not be found for the selected Governance`, + }) + } + } + fetchLpMintInfo() + }, [governedAccount?.governance.pubkey, form.liquidityPool]) + + return ( + <> + + + + handleSetForm({ + value: evt.target.value, + propertyName: 'amountIn', + }) + } + error={formErrors['amountIn']} + /> + + ) +} + +export default RaydiumRemoveLiquidityFromPool diff --git a/pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx b/pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx new file mode 100644 index 0000000000..d0c01d0f31 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx @@ -0,0 +1,153 @@ +import { Governance, ProgramAccount } from '@solana/spl-governance' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { Instructions } from '@utils/uiTypes/proposalCreationTypes' +import Clawback from 'VoteStakeRegistry/components/instructions/Clawback' +import Grant from 'VoteStakeRegistry/components/instructions/Grant' +import ProgramUpgrade from './bpfUpgradeableLoader/ProgramUpgrade' +import CreateAssociatedTokenAccount from './CreateAssociatedTokenAccount' +import CustomBase64 from './CustomBase64' +import Empty from './Empty' +import Mint from './Mint' +import RaydiumAddLiquidityToPool from './Raydium/AddLiquidityToPool' +import RaydiumRemoveLiquidityFromPool from './Raydium/RemoveLiquidityFromPool' +import SetProgramAuthority from './SetProgramAuthority' +import SplTokenTransfer from './SplTokenTransfer' +import SolendCreateObligationAccount from './Solend/CreateObligationAccount' +import SolendDepositReserveLiquidityAndObligationCollateral from './Solend/DepositReserveLiquidityAndObligationCollateral' +import SolendInitObligationAccount from './Solend/InitObligationAccount' +import SolendRefreshObligation from './Solend/RefreshObligation' +import SolendRefreshReserve from './Solend/RefreshReserve' +import SolendWithdrawObligationCollateralAndRedeemReserveLiquidity from './Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity' +import UXDDepositInsuranceToMangoDepository from './UXD/DepositInsuranceToMangoDepository' +import UXDInitializeController from './UXD/InitializeController' +import UXDRegisterMangoDeposiory from './UXD/RegisterMangoDepository' +import UXDSetMangoDepositoriesRedeemableSoftCap from './UXD/SetMangoDepositoriesRedeemableSoftCap' +import UXDSetRedeemableGlobalSupplyCap from './UXD/SetRedeemGlobalSupplyCap' +import UXDWithdrawInsuranceFromMangoDepository from './UXD/WithdrawInsuranceFromMangoDepository' + +const SelectedInstruction = ({ + itxType, + index, + governance, + governedAccount, +}: { + itxType: number + index: number + governance: ProgramAccount | null + governedAccount: GovernedMultiTypeAccount | undefined +}) => { + switch (itxType) { + case Instructions.Transfer: + return + case Instructions.ProgramUpgrade: + return + case Instructions.SetProgramAuthority: + return + case Instructions.CreateAssociatedTokenAccount: + return ( + + ) + case Instructions.SolendCreateObligationAccount: + return ( + + ) + case Instructions.SolendInitObligationAccount: + return ( + + ) + case Instructions.SolendDepositReserveLiquidityAndObligationCollateral: + return ( + + ) + case Instructions.SolendRefreshObligation: + return ( + + ) + case Instructions.SolendRefreshReserve: + return ( + + ) + case Instructions.SolendWithdrawObligationCollateralAndRedeemReserveLiquidity: + return ( + + ) + case Instructions.RaydiumAddLiquidity: + return ( + + ) + case Instructions.RaydiumRemoveLiquidity: + return ( + + ) + case Instructions.UXDInitializeController: + return + case Instructions.UXDSetRedeemableGlobalSupplyCap: + return ( + + ) + case Instructions.UXDSetMangoDepositoriesRedeemableSoftCap: + return ( + + ) + case Instructions.UXDRegisterMangoDepository: + return + case Instructions.UXDDepositInsuranceToMangoDepository: + return ( + + ) + case Instructions.UXDWithdrawInsuranceFromMangoDepository: + return ( + + ) + case Instructions.Mint: + return + case Instructions.Base64: + return + case Instructions.None: + return + case Instructions.Grant: + return + case Instructions.Clawback: + return + default: + return null + } +} + +export default SelectedInstruction diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx index 295df70717..64a705321f 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx @@ -5,7 +5,7 @@ import * as yup from 'yup' import { isFormValid } from '@utils/formValidation' import { UiInstruction, - ProgramAuthorityForm, + SetProgramAuthorityForm, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../new' import useGovernanceAssets from '@hooks/useGovernanceAssets' @@ -42,7 +42,7 @@ const SetProgramAuthority = ({ }) const shouldBeGoverned = index !== 0 && governance const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ + const [form, setForm] = useState({ governedAccount: undefined, accountId: programId?.toString(), destinationAuthority: '', diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/CreateObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/CreateObligationAccount.tsx index 5f8ce2ed40..49a47dcead 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/CreateObligationAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/CreateObligationAccount.tsx @@ -1,125 +1,48 @@ -import React, { useContext, useEffect, useState } from 'react' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React from 'react' import * as yup from 'yup' -import { PublicKey } from '@solana/web3.js' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' -import useRealm from '@hooks/useRealm' +;('@hooks/useGovernedMultiTypeAccounts') +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import { createObligationAccount } from '@tools/sdk/solend/createObligationAccount' -import { isFormValid } from '@utils/formValidation' -import { - CreateSolendObligationAccountForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' - -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { CreateSolendObligationAccountForm } from '@utils/uiTypes/proposalCreationTypes' const CreateObligationAccount = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount?: GovernedMultiTypeAccount }) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() + const { + wallet, + connection, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + }), + buildInstruction: async function () { + return createObligationAccount({ + fundingAddress: wallet!.publicKey!, + walletAddress: governedAccount!.governance.pubkey, + }) + }, + }) // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} } - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({}) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.governedAccount?.governance?.account || - !wallet?.publicKey - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await createObligationAccount({ - fundingAddress: wallet.publicKey, - walletAddress: form.governedAccount.governance.pubkey, - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, - }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - }) - return ( - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - ) + // only need governance select for this instruction + return null } export default CreateObligationAccount diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx index ac6be4ce2d..c45a0a3cb4 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx @@ -1,150 +1,69 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import BigNumber from 'bignumber.js' +import React from 'react' import * as yup from 'yup' - -import { BN } from '@project-serum/anchor' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' import Input from '@components/inputs/Input' import Select from '@components/inputs/Select' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' -import useRealm from '@hooks/useRealm' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import SolendConfiguration from '@tools/sdk/solend/configuration' import { depositReserveLiquidityAndObligationCollateral } from '@tools/sdk/solend/depositReserveLiquidityAndObligationCollateral' -import { isFormValid } from '@utils/formValidation' -import { - DepositReserveLiquidityAndObligationCollateralForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' -import useWalletStore from 'stores/useWalletStore' -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { DepositReserveLiquidityAndObligationCollateralForm } from '@utils/uiTypes/proposalCreationTypes' +import SelectOptionList from '../../SelectOptionList' +import { uiAmountToNativeBN } from '@tools/sdk/units' const DepositReserveLiquidityAndObligationCollateral = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount?: GovernedMultiTypeAccount }) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - // Hardcoded gate used to be clear about what cluster is supported for now - if (connection.cluster !== 'mainnet') { - return <>This instruction does not support {connection.cluster} - } - - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [ + const { form, - setForm, - ] = useState({ - uiAmount: '0', - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.governedAccount?.governance?.account || - !wallet?.publicKey || - !form.mintName - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await depositReserveLiquidityAndObligationCollateral({ - obligationOwner: form.governedAccount.governance.pubkey, - liquidityAmount: new BN( - new BigNumber(form.uiAmount) - .shiftedBy( + connection, + formErrors, + handleSetForm, + } = useInstructionFormBuilder( + { + index, + initialFormValues: { + governedAccount, + uiAmount: 0, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), + uiAmount: yup + .number() + .moreThan(0, 'Amount should be more than 0') + .required('Amount is required'), + }), + buildInstruction: async function () { + if (!form.mintName) + throw new Error('invalid form, missing mintName field') + return depositReserveLiquidityAndObligationCollateral({ + obligationOwner: governedAccount!.governance.pubkey, + liquidityAmount: uiAmountToNativeBN( + form.uiAmount, SolendConfiguration.getSupportedMintInformation(form.mintName) .decimals - ) - .toString() - ), - mintName: form.mintName, - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, + ), + mintName: form.mintName, + }) }, - index - ) - }, [form]) + } + ) - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - uiAmount: yup - .number() - .moreThan(0, 'Amount should be more than 0') - .required('Amount is required'), - }) + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } return ( <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - handleSetForm({ diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/InitObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/InitObligationAccount.tsx index 455cad275b..beb20aa4d3 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/InitObligationAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/InitObligationAccount.tsx @@ -1,127 +1,44 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' +import React from 'react' import * as yup from 'yup' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' - -import useRealm from '@hooks/useRealm' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import { initObligationAccount } from '@tools/sdk/solend/initObligationAccount' -import { isFormValid } from '@utils/formValidation' -import { - InitSolendObligationAccountForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' - -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { InitSolendObligationAccountForm } from '@utils/uiTypes/proposalCreationTypes' const InitObligationAccount = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount?: GovernedMultiTypeAccount }) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() + const { + connection, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + }), + buildInstruction: async function () { + return initObligationAccount({ + obligationOwner: governedAccount!.governance.pubkey, + }) + }, + }) // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} } - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({}) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.governedAccount?.governance?.account || - !wallet?.publicKey - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await initObligationAccount({ - obligationOwner: form.governedAccount.governance.pubkey, - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, - }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - }) - - return ( - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - ) + return null } export default InitObligationAccount diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx index 7e6da08836..f988018151 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx @@ -1,146 +1,63 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' +import React from 'react' import * as yup from 'yup' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' import Select from '@components/inputs/Select' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' -import useRealm from '@hooks/useRealm' -import SolendConfiguration from '@tools/sdk/solend/configuration' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import { refreshObligation } from '@tools/sdk/solend/refreshObligation' -import { isFormValid } from '@utils/formValidation' -import { - RefreshObligationForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' - -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { RefreshObligationForm } from '@utils/uiTypes/proposalCreationTypes' +import SelectOptionList from '../../SelectOptionList' +import { SOLEND_MINT_NAME_OPTIONS } from '@tools/sdk/solend/utils' const RefreshObligation = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount?: GovernedMultiTypeAccount }) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() + const { + form, + formErrors, + connection, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), + }), + buildInstruction: async function () { + if (!form.mintName) + throw new Error('invalid form, missing mintName field') + return refreshObligation({ + obligationOwner: governedAccount!.governance.pubkey, + mintNames: [form.mintName], + }) + }, + }) // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} } - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({}) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.governedAccount?.governance?.account || - !form.mintName || - !wallet?.publicKey - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await refreshObligation({ - obligationOwner: form.governedAccount.governance.pubkey, - mintNames: [form.mintName], - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, - }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - }) - return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - - - + ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx index a90c519d36..8227454413 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx @@ -1,144 +1,62 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' +import React from 'react' import * as yup from 'yup' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' -import { PublicKey } from '@solana/web3.js' import Select from '@components/inputs/Select' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' -import useRealm from '@hooks/useRealm' -import SolendConfiguration from '@tools/sdk/solend/configuration' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import { refreshReserve } from '@tools/sdk/solend/refreshReserve' -import { isFormValid } from '@utils/formValidation' -import { - RefreshReserveForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' - -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { RefreshReserveForm } from '@utils/uiTypes/proposalCreationTypes' +import SelectOptionList from '../../SelectOptionList' +import { SOLEND_MINT_NAME_OPTIONS } from '@tools/sdk/solend/utils' const RefreshReserve = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount?: GovernedMultiTypeAccount }) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - const shouldBeGoverned = index !== 0 && governance - - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({}) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) + const { + form, + connection, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), + }), + buildInstruction: async function () { + if (!form.mintName) + throw new Error('invalid form, missing mintName field') + return refreshReserve({ + mintName: form.mintName, + }) + }, + }) // Hardcoded gate used to be clear about what cluster is supported for now if (connection.cluster !== 'mainnet') { return <>This instruction does not support {connection.cluster} } - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.mintName || - !form.governedAccount?.governance.account || - !wallet?.publicKey - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await refreshReserve({ - mintName: form.mintName, - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount?.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, - }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - }) - return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - - + ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx index 90213d5c53..e8cc0977c4 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx @@ -1,154 +1,75 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' +import React from 'react' import Input from '@components/inputs/Input' -import BigNumber from 'bignumber.js' import * as yup from 'yup' -import { BN } from '@project-serum/anchor' -import { - Governance, - ProgramAccount, - serializeInstructionToBase64, -} from '@solana/spl-governance' import { PublicKey } from '@solana/web3.js' import Select from '@components/inputs/Select' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' -import useRealm from '@hooks/useRealm' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import SolendConfiguration from '@tools/sdk/solend/configuration' import { withdrawObligationCollateralAndRedeemReserveLiquidity } from '@tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - WithdrawObligationCollateralAndRedeemReserveLiquidityForm, -} from '@utils/uiTypes/proposalCreationTypes' - -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../../new' -import GovernedAccountSelect from '../../GovernedAccountSelect' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { WithdrawObligationCollateralAndRedeemReserveLiquidityForm } from '@utils/uiTypes/proposalCreationTypes' +import SelectOptionList from '../../SelectOptionList' +import { uiAmountToNativeBN } from '@tools/sdk/units' +import { SOLEND_MINT_NAME_OPTIONS } from '@tools/sdk/solend/utils' const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount?: GovernedMultiTypeAccount }) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - - // Hardcoded gate used to be clear about what cluster is supported for now - if (connection.cluster !== 'mainnet') { - return <>This instruction does not support {connection.cluster} - } - - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [ + const { form, - setForm, - ] = useState({ - uiAmount: '0', - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - - async function getInstruction(): Promise { - const isValid = await validateInstruction() - - if ( - !connection || - !isValid || - !programId || - !form.governedAccount?.governance?.account || - !wallet?.publicKey || - !form.mintName - ) { - return { - serializedInstruction: '', - isValid: false, - governance: form.governedAccount?.governance, - } - } - - const tx = await withdrawObligationCollateralAndRedeemReserveLiquidity({ - obligationOwner: form.governedAccount.governance.pubkey, - liquidityAmount: new BN( - new BigNumber(form.uiAmount) - .shiftedBy( + connection, + formErrors, + handleSetForm, + } = useInstructionFormBuilder( + { + index, + initialFormValues: { + governedAccount, + uiAmount: 0, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), + uiAmount: yup + .number() + .moreThan(0, 'Amount should be more than 0') + .required('Amount is required'), + }), + buildInstruction: async function () { + if (!form.mintName) + throw new Error('invalid form, missing mintName field') + + return withdrawObligationCollateralAndRedeemReserveLiquidity({ + obligationOwner: governedAccount!.governance.pubkey, + liquidityAmount: uiAmountToNativeBN( + form.uiAmount, SolendConfiguration.getSupportedMintInformation(form.mintName) .decimals - ) - .toString() - ), - mintName: form.mintName, - ...(form.destinationLiquidity && { - destinationLiquidity: new PublicKey(form.destinationLiquidity), - }), - }) - - return { - serializedInstruction: serializeInstructionToBase64(tx), - isValid: true, - governance: form.governedAccount.governance, - } - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [programId]) - - useEffect(() => { - handleSetInstructions( - { - governedAccount: form.governedAccount?.governance, - getInstruction, + ), + mintName: form.mintName, + ...(form.destinationLiquidity && { + destinationLiquidity: new PublicKey(form.destinationLiquidity), + }), + }) }, - index - ) - }, [form]) + } + ) - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - uiAmount: yup - .number() - .moreThan(0, 'Amount should be more than 0') - .required('Amount is required'), - }) + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } return ( <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - handleSetForm({ diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx deleted file mode 100644 index 7239061a09..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/AddLiquidity.tsx +++ /dev/null @@ -1,243 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' -import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - AddLiquidityRaydiumForm, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' -import useWalletStore from 'stores/useWalletStore' -import Input from '@components/inputs/Input' -import { debounce } from '@utils/debounce' -import Select from '@components/inputs/Select' -import { - ProgramAccount, - serializeInstructionToBase64, - Governance, -} from '@solana/spl-governance' -import GovernedAccountSelect from '../../GovernedAccountSelect' -import { createAddLiquidityInstruction } from '@tools/sdk/raydium/createAddLiquidityInstruction' -import { - getAmountOut, - getLiquidityPoolKeysByLabel, -} from '@tools/sdk/raydium/helpers' -import { liquidityPoolKeysList } from '@tools/sdk/raydium/poolKeys' -import { BN } from '@project-serum/anchor' -import BigNumber from 'bignumber.js' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' - -const AddLiquidityRaydium = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - liquidityPool: '', - baseAmountIn: 0, - quoteAmountIn: 0, - fixedSide: 'base', - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - async function getInstruction(): Promise { - const isValid = await validateInstruction() - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - wallet?.publicKey - ) { - const poolKeys = getLiquidityPoolKeysByLabel(form.liquidityPool) - const [base, quote] = await Promise.all([ - connection.current.getTokenSupply(poolKeys.baseMint), - connection.current.getTokenSupply(poolKeys.quoteMint), - ]) - - const createIx = createAddLiquidityInstruction( - poolKeys, - new BN( - new BigNumber(form.baseAmountIn.toString()) - .shiftedBy(base.value.decimals) - .toString() - ), - new BN( - new BigNumber(form.quoteAmountIn.toString()) - .shiftedBy(quote.value.decimals) - .toString() - ), - form.fixedSide, - form.governedAccount.governance.pubkey - ) - serializedInstruction = serializeInstructionToBase64(createIx) - } - const obj: UiInstruction = { - serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - if (form.baseAmountIn) { - debounce.debounceFcn(async () => { - handleSetForm({ - value: await getAmountOut( - form.liquidityPool, - form.baseAmountIn, - connection - ), - propertyName: 'quoteAmountIn', - }) - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.baseAmountIn]) - - useEffect(() => { - isFormValid(schema, form).then(({ validationErrors }) => { - setFormErrors(validationErrors) - }) - }, [form.quoteAmountIn]) - - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), - liquidityPool: yup.string().required('Liquidity Pool is required'), - baseAmountIn: yup - .number() - .moreThan(0, 'Amount for Base token should be more than 0') - .required('Amount for Base token is required'), - quoteAmountIn: yup - .number() - .moreThan(0, 'Amount for Quote token should be more than 0') - .required('Amount for Quote token is required'), - fixedSide: yup - .string() - .equals(['base', 'quote']) - .required('Fixed Side is required'), - }) - - return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - - - {form.liquidityPool ? ( - <> - - handleSetForm({ - value: evt.target.value, - propertyName: 'baseAmountIn', - }) - } - error={formErrors['baseAmountIn']} - /> - - - handleSetForm({ - value: Number(evt.target.value), - propertyName: 'quoteAmountIn', - }) - } - disabled={true} - error={formErrors['quoteAmountIn']} - /> - - - ) : null} - - ) -} - -export default AddLiquidityRaydium diff --git a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx deleted file mode 100644 index d873df2fde..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/raydium/RemoveLiquidity.tsx +++ /dev/null @@ -1,210 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' -import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - RemoveLiquidityRaydiumForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' -import useWalletStore from 'stores/useWalletStore' -import Input from '@components/inputs/Input' -import { debounce } from '@utils/debounce' -import Select from '@components/inputs/Select' -import { - ProgramAccount, - serializeInstructionToBase64, - Governance, -} from '@solana/spl-governance' -import GovernedAccountSelect from '../../GovernedAccountSelect' -import { getLPMintInfo } from '@tools/sdk/raydium/helpers' -import { liquidityPoolKeysList } from '@tools/sdk/raydium/poolKeys' -import { createRemoveLiquidityInstruction } from '@tools/sdk/raydium/createRemoveLiquidityInstruction' -import BigNumber from 'bignumber.js' -import { jsonInfo2PoolKeys } from '@raydium-io/raydium-sdk' -import { notify } from '@utils/notifications' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' - -const RemoveLiquidityRaydium = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - liquidityPool: '', - amountIn: 0, - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - - const [lpMintInfo, setLpMintInfo] = useState<{ - balance: number - decimals: number - } | null>(null) - useEffect(() => { - const fetchLpData = async () => { - if (!form.governedAccount?.governance.pubkey || !form.liquidityPool) - return - const { lpMint } = liquidityPoolKeysList[form.liquidityPool] - try { - const { maxBalance, decimals } = await getLPMintInfo( - connection, - new PublicKey(lpMint), - form.governedAccount.governance.pubkey - ) - setLpMintInfo({ balance: maxBalance, decimals }) - } catch (e) { - notify({ - type: 'error', - message: 'Could not fetch LP Account', - description: `${form.liquidityPool} LP Token Account could not be found for the selected Governance`, - }) - } - } - fetchLpData() - }, [form.governedAccount?.governance.pubkey, form.liquidityPool]) - - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - async function getInstruction(): Promise { - const isValid = await validateInstruction() - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - form.liquidityPool && - lpMintInfo && - wallet?.publicKey - ) { - const createIx = createRemoveLiquidityInstruction( - new PublicKey(form.governedAccount.governance.pubkey), - jsonInfo2PoolKeys(liquidityPoolKeysList[form.liquidityPool]), - new BigNumber(form.amountIn).shiftedBy(lpMintInfo.decimals).toString() - ) - serializedInstruction = serializeInstructionToBase64(createIx) - } - const obj: UiInstruction = { - serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - if (form.liquidityPool) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - handleSetForm({ - value: form.liquidityPool, - propertyName: 'liquidityPool', - }) - } - }, [form.liquidityPool]) - - useEffect(() => { - if (form.amountIn) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.amountIn]) - - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), - liquidityPool: yup.string().required('Liquidity Pool is required'), - amountIn: yup - .number() - .moreThan(0, 'Amount for LP token should be more than 0') - .required('Amount for LP token is required'), - }) - - return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - - - - handleSetForm({ - value: evt.target.value, - propertyName: 'amountIn', - }) - } - error={formErrors['amountIn']} - /> - - ) -} - -export default RemoveLiquidityRaydium diff --git a/pages/dao/[symbol]/proposal/new.tsx b/pages/dao/[symbol]/proposal/new.tsx index 6152f0fb98..30b7167d00 100644 --- a/pages/dao/[symbol]/proposal/new.tsx +++ b/pages/dao/[symbol]/proposal/new.tsx @@ -12,7 +12,6 @@ import { Governance, GovernanceAccountType, ProgramAccount, - RpcContext, } from '@solana/spl-governance' import { PublicKey } from '@solana/web3.js' import Button, { LinkButton, SecondaryButton } from '@components/Button' @@ -20,11 +19,10 @@ import Input from '@components/inputs/Input' import Select from '@components/inputs/Select' import Textarea from '@components/inputs/Textarea' import TokenBalanceCardWrapper from '@components/TokenBalance/TokenBalanceCardWrapper' +import useCreateProposal from '@hooks/useCreateProposal' import useGovernanceAssets from '@hooks/useGovernanceAssets' import useQueryContext from '@hooks/useQueryContext' import useRealm from '@hooks/useRealm' -import { getProgramVersionForRealm } from '@models/registry/api' - import { getTimestampFromDays } from '@tools/sdk/units' import { formValidation, isFormValid } from '@utils/formValidation' import { @@ -34,30 +32,12 @@ import { UiInstruction, } from '@utils/uiTypes/proposalCreationTypes' -import { createProposal } from 'actions/createProposal' import useWalletStore from 'stores/useWalletStore' import { notify } from 'utils/notifications' -import Clawback from 'VoteStakeRegistry/components/instructions/Clawback' -import Grant from 'VoteStakeRegistry/components/instructions/Grant' -import useVoteStakeRegistryClientStore from 'VoteStakeRegistry/stores/voteStakeRegistryClientStore' import InstructionContentContainer from './components/InstructionContentContainer' -import ProgramUpgrade from './components/instructions/bpfUpgradeableLoader/ProgramUpgrade' -import CreateAssociatedTokenAccount from './components/instructions/CreateAssociatedTokenAccount' -import CustomBase64 from './components/instructions/CustomBase64' -import Empty from './components/instructions/Empty' -import MakeChangeMaxAccounts from './components/instructions/Mango/MakeChangeMaxAccounts' -import MakeChangeReferralFeeParams from './components/instructions/Mango/MakeChangeReferralFeeParams' -import Mint from './components/instructions/Mint' -import SplTokenTransfer from './components/instructions/SplTokenTransfer' import VoteBySwitch from './components/VoteBySwitch' -import FriktionDeposit from './components/instructions/Friktion/FriktionDeposit' -import CreateObligationAccount from './components/instructions/Solend/CreateObligationAccount' -import DepositReserveLiquidityAndObligationCollateral from './components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral' -import InitObligationAccount from './components/instructions/Solend/InitObligationAccount' -import RefreshObligation from './components/instructions/Solend/RefreshObligation' -import RefreshReserve from './components/instructions/Solend/RefreshReserve' -import WithdrawObligationCollateralAndRedeemReserveLiquidity from './components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity' +import ProposalForm from './components/instructions/ProposalForm' const schema = yup.object().shape({ title: yup.string().required('Title is required'), @@ -83,23 +63,12 @@ function extractGovernanceAccountFromInstructionsData( const New = () => { const router = useRouter() - const client = useVoteStakeRegistryClientStore((s) => s.state.client) + const { handleCreateProposal } = useCreateProposal() const { fmtUrlWithCluster } = useQueryContext() - const { - symbol, - realm, - realmInfo, - realmDisplayName, - ownVoterWeight, - mint, - councilMint, - canChooseWhoVote, - } = useRealm() + const { symbol, realm, realmDisplayName, canChooseWhoVote } = useRealm() const { getAvailableInstructions } = useGovernanceAssets() const availableInstructions = getAvailableInstructions() - const wallet = useWalletStore((s) => s.current) - const connection = useWalletStore((s) => s.connection) const { fetchRealmGovernance, fetchTokenAccountsForSelectedRealmGovernances, @@ -210,14 +179,6 @@ const New = () => { handleTurnOffLoaders() throw Error('No governance selected') } - - const rpcContext = new RpcContext( - new PublicKey(realm.owner.toString()), - getProgramVersionForRealm(realmInfo!), - wallet!, - connection.current, - connection.endpoint - ) const instructionsData = instructions.map((x) => { return { data: x.serializedInstruction @@ -239,39 +200,14 @@ const New = () => { governance.pubkey )) as ProgramAccount - const ownTokenRecord = ownVoterWeight.getTokenRecordToCreateProposal( - governance.account.config - ) - const defaultProposalMint = !mint?.supply.isZero() - ? realm.account.communityMint - : !councilMint?.supply.isZero() - ? realm.account.config.councilMint - : undefined - - const proposalMint = - canChooseWhoVote && voteByCouncil - ? realm.account.config.councilMint - : defaultProposalMint - - if (!proposalMint) { - throw new Error( - 'There is no suitable governing token for the proposal' - ) - } - - proposalAddress = await createProposal( - rpcContext, - realm, - selectedGovernance.pubkey, - ownTokenRecord.pubkey, - form.title, - form.description, - proposalMint, - selectedGovernance?.account?.proposalCount, + proposalAddress = await handleCreateProposal({ + title: form.title, + description: form.description, + governance: selectedGovernance, instructionsData, + voteByCouncil, isDraft, - client - ) + }) const url = fmtUrlWithCluster( `/dao/${symbol}/proposal/${proposalAddress}` @@ -303,76 +239,6 @@ const New = () => { fetchTokenAccountsForSelectedRealmGovernances() }, []) - const getCurrentInstruction = ({ typeId, idx }) => { - switch (typeId) { - case Instructions.Transfer: - return ( - - ) - case Instructions.ProgramUpgrade: - return ( - - ) - case Instructions.CreateAssociatedTokenAccount: - return ( - - ) - case Instructions.DepositIntoVolt: - return - case Instructions.CreateSolendObligationAccount: - return - case Instructions.InitSolendObligationAccount: - return - case Instructions.DepositReserveLiquidityAndObligationCollateral: - return ( - - ) - case Instructions.RefreshSolendObligation: - return - case Instructions.RefreshSolendReserve: - return - case Instructions.WithdrawObligationCollateralAndRedeemReserveLiquidity: - return ( - - ) - case Instructions.Mint: - return - case Instructions.Base64: - return - case Instructions.None: - return - case Instructions.MangoMakeChangeMaxAccounts: - return ( - - ) - case Instructions.MangoChangeReferralFeeParams: - return ( - - ) - case Instructions.Grant: - return - case Instructions.Clawback: - return - default: - null - } - } - return (

{ }} >

Instructions

- {instructionsData.map((instruction, idx) => { const availableInstructionsForIdx = getAvailableInstructionsForIndex( idx @@ -463,7 +328,7 @@ const New = () => { value={instruction.type?.name} > {availableInstructionsForIdx.map((inst) => ( - + {inst.name} ))} @@ -473,10 +338,11 @@ const New = () => { idx={idx} instructionsData={instructionsData} > - {getCurrentInstruction({ - typeId: instruction.type?.id, - idx, - })} + {idx !== 0 && ( { const [lpTokenAccount] = findATAAddrSync(owner, poolKeys.lpMint) const [baseTokenAccount] = findATAAddrSync(owner, poolKeys.baseMint) @@ -20,7 +20,7 @@ export const createRemoveLiquidityInstruction = ( lpTokenAccount, owner, }, - amountIn: new BN(amountIn), + amountIn, }) return itx diff --git a/tools/sdk/raydium/helpers.ts b/tools/sdk/raydium/helpers.ts index 44111cbf2c..f5ba4adc56 100644 --- a/tools/sdk/raydium/helpers.ts +++ b/tools/sdk/raydium/helpers.ts @@ -1,60 +1,61 @@ +import BigNumber from 'bignumber.js' import { BN } from '@project-serum/anchor' import { + jsonInfo2PoolKeys, Liquidity, LiquidityPoolKeys, Percent, - TokenAmount, Token, - jsonInfo2PoolKeys, + TokenAmount, } from '@raydium-io/raydium-sdk' -import { PublicKey } from '@solana/web3.js' -import { ConnectionContext } from '@utils/connection' -import { findATAAddrSync } from '@uxdprotocol/uxd-client' -import BigNumber from 'bignumber.js' +import { Connection, PublicKey } from '@solana/web3.js' +import { findATAAddrSync } from '@utils/ataTools' import { liquidityPoolKeysList } from './poolKeys' export const getAmountOut = async ( liquidityPool: string, amountIn: number, - connection: ConnectionContext + connection: Connection, + slippage: number //slippage in % ) => { + if (amountIn <= 0) return 0 const poolKeys = getLiquidityPoolKeysByLabel(liquidityPool) const [base, quote] = await Promise.all([ - connection.current.getTokenSupply(poolKeys.baseMint), - connection.current.getTokenSupply(poolKeys.quoteMint), + connection.getTokenSupply(poolKeys.baseMint), + connection.getTokenSupply(poolKeys.quoteMint), ]) const amountInBN = new BN( new BigNumber(Number(amountIn).toFixed(base.value.decimals)) .shiftedBy(base.value.decimals) .toString() ) - const amountOut = Liquidity.computeCurrencyAmountOut({ + const { minAmountOut } = Liquidity.computeAmountOut({ poolKeys, poolInfo: await Liquidity.fetchInfo({ - connection: connection.current, + connection: connection, poolKeys, }), - currencyAmountIn: new TokenAmount( + amountIn: new TokenAmount( new Token(poolKeys.baseMint, base.value.decimals), amountInBN ), currencyOut: new Token(poolKeys.quoteMint, quote.value.decimals), - slippage: new Percent(10, 1000), + slippage: new Percent(new BN(slippage), 10), // slippage in 1/1000 }) - const currentPrice = amountOut.currentPrice.toFixed(quote.value.decimals) + const currentPrice = minAmountOut.toFixed(quote.value.decimals) return Number(currentPrice) * amountIn } export const getLPMintInfo = async ( - connection: ConnectionContext, + connection: Connection, lpMint: PublicKey, user: PublicKey ) => { const [lpTokenAccount] = findATAAddrSync(user, lpMint) const [lpInfo, lpUserBalance] = await Promise.all([ - connection.current.getTokenSupply(lpMint), - connection.current.getTokenAccountBalance(lpTokenAccount), + connection.getTokenSupply(lpMint), + connection.getTokenAccountBalance(lpTokenAccount), ]) return { lpTokenAccount, @@ -71,3 +72,17 @@ export const getLiquidityPoolKeysByLabel = ( return jsonInfo2PoolKeys(liquidityPoolKeysList[lp]) } + +export const fetchLiquidityPoolData = async ({ + governanceKey, + lp, + connection, +}: { + governanceKey?: PublicKey + lp?: string + connection: Connection +}) => { + if (!governanceKey || !lp) return { maxBalance: 0, decimals: 0 } + const { lpMint } = liquidityPoolKeysList[lp] + return getLPMintInfo(connection, new PublicKey(lpMint), governanceKey) +} diff --git a/tools/sdk/solend/utils.ts b/tools/sdk/solend/utils.ts index 3c60aa982b..4ae4c9736e 100644 --- a/tools/sdk/solend/utils.ts +++ b/tools/sdk/solend/utils.ts @@ -11,3 +11,5 @@ export async function deriveObligationAddressFromWalletAndSeed( SolendConfiguration.programID ) } + +export const SOLEND_MINT_NAME_OPTIONS = SolendConfiguration.getSupportedMintNames() diff --git a/tools/sdk/units.ts b/tools/sdk/units.ts index f35b4c60da..3554b1ed61 100644 --- a/tools/sdk/units.ts +++ b/tools/sdk/units.ts @@ -155,3 +155,13 @@ export function getMintSupplyFractionAsDecimalPercentage( .dividedBy(new BigNumber(mint.supply.toString())) .toNumber() } + +export function uiAmountToNativeBN( + uiAmount: number | string, + decimals: number +): BN { + const amount = typeof uiAmount === 'number' ? uiAmount.toString() : uiAmount + return new BN( + new BigNumber(amount).shiftedBy(decimals).integerValue().toString() + ) +} diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index 95d6596b67..dc3b2b73fb 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -1,11 +1,8 @@ -import { AmountSide } from '@raydium-io/raydium-sdk' import { Governance, InstructionData } from '@solana/spl-governance' import { ProgramAccount } from '@solana/spl-governance' import { RpcContext } from '@solana/spl-governance' import { MintInfo } from '@solana/spl-token' import { PublicKey, Keypair, TransactionInstruction } from '@solana/web3.js' -import { SupportedMintName } from '@tools/sdk/solend/configuration' -import { SplTokenUIName } from '@utils/splTokens' import { getNameOf } from '@tools/core/script' import { GovernedMintInfoAccount, @@ -13,8 +10,11 @@ import { GovernedProgramAccount, GovernedTokenAccount, } from '@utils/tokens' +import { SupportedMintName } from '@tools/sdk/solend/configuration' +import { SplTokenUIName } from '@utils/splTokens' import { DepositWithMintAccount, Voter } from 'VoteStakeRegistry/sdk/accounts' import { LockupKind } from 'VoteStakeRegistry/tools/types' +import { AmountSide } from '@raydium-io/raydium-sdk' export interface UiInstruction { serializedInstruction: string @@ -86,49 +86,21 @@ export interface ProgramUpgradeForm { bufferSpillAddress?: string | undefined } -export interface ProgramAuthorityForm { +export const programUpgradeFormNameOf = getNameOf() + +export interface SetProgramAuthorityForm { governedAccount: GovernedProgramAccount | GovernedTokenAccount | undefined accountId: string | undefined destinationAuthority: string } - -export interface AddLiquidityRaydiumForm { - governedAccount: GovernedMultiTypeAccount | undefined - liquidityPool: string - baseAmountIn: number - quoteAmountIn: number - fixedSide: AmountSide -} - -export interface RemoveLiquidityRaydiumForm { - governedAccount: GovernedMultiTypeAccount | undefined - liquidityPool: string - amountIn: number -} -export const programUpgradeFormNameOf = getNameOf() - -export interface MangoMakeChangeMaxAccountsForm { - governedAccount: GovernedProgramAccount | undefined - programId: string | undefined - mangoGroupKey: string | undefined - maxMangoAccounts: number -} -export interface MangoMakeChangeReferralFeeParams { - governedAccount: GovernedProgramAccount | undefined - programId: string | undefined - mangoGroupKey: string | undefined - refSurchargeCentibps: number - refShareCentibps: number - refMngoRequired: number -} export interface Base64InstructionForm { - governedAccount: GovernedMultiTypeAccount | undefined + governedAccount?: GovernedMultiTypeAccount base64: string holdUpTime: number } export interface EmptyInstructionForm { - governedAccount: GovernedMultiTypeAccount | undefined + governedAccount?: GovernedMultiTypeAccount } export interface CreateAssociatedTokenAccountForm { @@ -146,13 +118,13 @@ export interface InitSolendObligationAccountForm { export interface DepositReserveLiquidityAndObligationCollateralForm { governedAccount?: GovernedMultiTypeAccount - uiAmount: string + uiAmount: number mintName?: SupportedMintName } export interface WithdrawObligationCollateralAndRedeemReserveLiquidityForm { governedAccount?: GovernedMultiTypeAccount - uiAmount: string + uiAmount: number mintName?: SupportedMintName destinationLiquidity?: string } @@ -167,33 +139,19 @@ export interface RefreshReserveForm { mintName?: SupportedMintName } -export enum Instructions { - Transfer, - ProgramUpgrade, - SetProgramAuthority, - Mint, - Base64, - None, - AddLiquidityRaydium, - RemoveLiquidityRaydium, - InitializeController, - SetRedeemableGlobalSupplyCap, - SetMangoDepositoriesRedeemableSoftCap, - RegisterMangoDepository, - DepositInsuranceToMangoDepository, - WithdrawInsuranceFromMangoDepository, - MangoMakeChangeMaxAccounts, - MangoChangeReferralFeeParams, - CreateAssociatedTokenAccount, - DepositIntoVolt, - CreateSolendObligationAccount, - InitSolendObligationAccount, - DepositReserveLiquidityAndObligationCollateral, - WithdrawObligationCollateralAndRedeemReserveLiquidity, - RefreshSolendObligation, - RefreshSolendReserve, - Grant, - Clawback, +export interface AddLiquidityRaydiumForm { + governedAccount?: GovernedMultiTypeAccount + liquidityPool?: string + baseAmountIn?: number + quoteAmountIn?: number + fixedSide: AmountSide + slippage: number +} + +export interface RemoveLiquidityRaydiumForm { + governedAccount?: GovernedMultiTypeAccount + liquidityPool: string + amountIn: number } export interface InitializeControllerForm { @@ -237,23 +195,31 @@ export interface WithdrawInsuranceFromMangoDepositoryForm { programId: string | undefined } -export enum UXDIntructions { - InitializeController, - SetRedeemableGlobalSupplyCap, - SetMangoDepositoriesRedeemableSoftCap, - RegisterMangoDepository, - DepositInsuranceToMangoDepository, - WithdrawInsuranceFromMangoDepository, +export enum Instructions { + Transfer, + ProgramUpgrade, + SetProgramAuthority, + Mint, + Base64, + None, Grant, Clawback, CreateAssociatedTokenAccount, - DepositIntoVolt, - CreateSolendObligationAccount, - InitSolendObligationAccount, - DepositReserveLiquidityAndObligationCollateral, - WithdrawObligationCollateralAndRedeemReserveLiquidity, - RefreshSolendObligation, - RefreshSolendReserve, + FriktionDepositIntoVolt, + RaydiumAddLiquidity, + RaydiumRemoveLiquidity, + SolendCreateObligationAccount, + SolendInitObligationAccount, + SolendDepositReserveLiquidityAndObligationCollateral, + SolendWithdrawObligationCollateralAndRedeemReserveLiquidity, + SolendRefreshObligation, + SolendRefreshReserve, + UXDInitializeController, + UXDSetRedeemableGlobalSupplyCap, + UXDSetMangoDepositoriesRedeemableSoftCap, + UXDRegisterMangoDepository, + UXDDepositInsuranceToMangoDepository, + UXDWithdrawInsuranceFromMangoDepository, } export type createParams = [ @@ -271,13 +237,13 @@ export type createParams = [ ] export interface ComponentInstructionData { - governedAccount?: ProgramAccount | undefined + governedAccount?: ProgramAccount getInstruction?: () => Promise type: any } export interface InstructionsContext { instructionsData: ComponentInstructionData[] handleSetInstructions: (val, index) => void - governance: ProgramAccount | null | undefined + governance?: ProgramAccount | null setGovernance: (val) => void } diff --git a/yarn.lock b/yarn.lock index 2e9e60b971..bdf55d9f79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -647,6 +647,11 @@ "@solana/spl-token" "^0.1.8" typescript "^4.5.4" +"@colors/colors@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" @@ -1870,15 +1875,18 @@ assert "^2.0.0" buffer "^6.0.1" -"@raydium-io/raydium-sdk@^1.0.1-beta.26": - version "1.0.1-beta.26" - resolved "https://registry.yarnpkg.com/@raydium-io/raydium-sdk/-/raydium-sdk-1.0.1-beta.26.tgz#acbc63089713eb6606709aa6db0f49d1ed50b7ea" - integrity sha512-p+nY3gX+iroCfLSu198+Wzc65Ls5ygmGNQK0Liy5+F2A0X+kEKvhikM9QXZvAWmxGAh72iPbRGLpWEAw+GsCww== +"@raydium-io/raydium-sdk@^1.0.1-beta.34": + version "1.0.1-beta.46" + resolved "https://registry.yarnpkg.com/@raydium-io/raydium-sdk/-/raydium-sdk-1.0.1-beta.46.tgz#9464fc0065d2da33246372174b8f53e62310e4d8" + integrity sha512-gndrKr+JfBP8pA6Gt1zrHeUDef3EdsOIenuuWUg6yyIVdcFmoj8ZwU+tmMOtz965sHt1SxDZT/81ej4FphtM1w== dependencies: + "@colors/colors" "^1.5.0" "@solana/buffer-layout" "^3.0.0" "@solana/spl-token" "^0.1.8" big.js "^6.1.1" decimal.js-light "^2.5.1" + fecha "^4.2.1" + lodash "^4.17.21" toformat "^2.0.0" "@reach/dialog@^0.16.2": @@ -2318,7 +2326,7 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solana/web3.js@^1.29.2": +"@solana/web3.js@^1.29.2", "@solana/web3.js@^1.34.0": version "1.36.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.36.0.tgz#79d7d5217b49b80139f4de68953adc5b9a9a264f" integrity sha512-RNT1451iRR7TyW7EJKMCrH/0OXawIe4zVm0DWQASwXlR/u1jmW6FrmH0lujIh7cGTlfOVbH+2ZU9AVUPLBFzwA== @@ -5460,6 +5468,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fecha@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.1.tgz#0a83ad8f86ef62a091e22bb5a039cd03d23eecce" + integrity sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q== + figures@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" From aa606e35eaaea4f54ee79f01f71262f2154d8f88 Mon Sep 17 00:00:00 2001 From: Sjillen Date: Tue, 29 Mar 2022 21:16:03 +0900 Subject: [PATCH 118/204] refactor UXD + generics instruction forms (#51) * refactor UXD intruction forms * update instruction forms - wip * fix linter error from the void * update from comments --- components/AssetsList/UpgradeProgram.tsx | 4 +- hooks/useInstructionFormBuilder.ts | 8 +- .../CreateAssociatedTokenAccount.tsx | 7 +- .../components/instructions/Empty.tsx | 74 ++---- .../components/instructions/ProposalForm.tsx | 11 - .../Raydium/AddLiquidityToPool.tsx | 8 +- .../Raydium/RemoveLiquidityFromPool.tsx | 4 +- .../instructions/SelectedInstruction.tsx | 30 ++- .../instructions/SetProgramAuthority.tsx | 168 ++++--------- ...eserveLiquidityAndObligationCollateral.tsx | 6 +- .../instructions/Solend/RefreshObligation.tsx | 4 +- .../instructions/Solend/RefreshReserve.tsx | 4 +- ...ionCollateralAndRedeemReserveLiquidity.tsx | 7 +- .../UXD/DepositInsuranceToMangoDepository.tsx | 203 ++++----------- .../instructions/UXD/InitializeController.tsx | 147 +++-------- .../UXD/RegisterMangoDepository.tsx | 184 ++++---------- .../SetMangoDepositoriesRedeemableSoftCap.tsx | 180 ++++---------- .../UXD/SetRedeemGlobalSupplyCap.tsx | 178 ++++--------- .../WithdrawInsuranceFromMangoDepository.tsx | 195 ++++----------- .../bpfUpgradeableLoader/ProgramUpgrade.tsx | 233 ++++++------------ utils/uiTypes/proposalCreationTypes.ts | 43 ++-- 21 files changed, 460 insertions(+), 1238 deletions(-) diff --git a/components/AssetsList/UpgradeProgram.tsx b/components/AssetsList/UpgradeProgram.tsx index 447bcda8f3..006459ed6d 100644 --- a/components/AssetsList/UpgradeProgram.tsx +++ b/components/AssetsList/UpgradeProgram.tsx @@ -63,7 +63,6 @@ const UpgradeProgram = ({ const programId: PublicKey | undefined = realmInfo?.programId const [form, setForm] = useState({ governedAccount: governedAccount, - programId: programId?.toString(), bufferAddress: '', description: '', title: '', @@ -114,7 +113,8 @@ const UpgradeProgram = ({ isValid && programId && form.governedAccount?.governance?.account && - wallet?.publicKey + wallet?.publicKey && + form.bufferAddress ) { const upgradeIx = await createUpgradeInstruction( form.governedAccount.governance.account.governedAccount, diff --git a/hooks/useInstructionFormBuilder.ts b/hooks/useInstructionFormBuilder.ts index 1407b2f44a..03f4d96d37 100644 --- a/hooks/useInstructionFormBuilder.ts +++ b/hooks/useInstructionFormBuilder.ts @@ -51,7 +51,7 @@ function useInstructionFormBuilder< if ( !wallet?.publicKey || !form.governedAccount?.governance?.account || - !buildInstruction || + //!buildInstruction || !(await validateForm()) ) { return { @@ -63,9 +63,9 @@ function useInstructionFormBuilder< try { return { - serializedInstruction: serializeInstructionToBase64( - await buildInstruction() - ), + serializedInstruction: buildInstruction + ? serializeInstructionToBase64(await buildInstruction()) + : '', isValid: true, governance: form.governedAccount?.governance, } diff --git a/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx index 8409a6ba20..13b84461d6 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx @@ -33,18 +33,15 @@ const CreateAssociatedTokenAccount = ({ splTokenMintUIName: yup.string().required('SPL Token Mint is required'), }), buildInstruction: async function () { - if (!form.splTokenMintUIName || !wallet?.publicKey) - throw new Error('invalid form, missing splTokenMintUiName field') - const [tx] = await createAssociatedTokenAccount( // fundingAddress - wallet.publicKey, + wallet!.publicKey!, // walletAddress governedAccount!.governance.pubkey, // splTokenMintAddress - getSplTokenMintAddressByUIName(form.splTokenMintUIName) + getSplTokenMintAddressByUIName(form.splTokenMintUIName!) ) return tx }, diff --git a/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx b/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx index e5929be873..ab774b5037 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Empty.tsx @@ -1,67 +1,29 @@ -import React, { useContext, useEffect, useState } from 'react' import * as yup from 'yup' -import { Governance, ProgramAccount } from '@solana/spl-governance' -import { validateInstruction } from '@utils/instructionTools' -import { - EmptyInstructionForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../new' -import GovernedAccountSelect from '../GovernedAccountSelect' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' +import { EmptyInstructionForm } from '@utils/uiTypes/proposalCreationTypes' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import { GovernedMultiTypeAccount } from '@utils/tokens' + const Empty = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount: GovernedMultiTypeAccount | undefined }) => { - const [form, setForm] = useState({ - governedAccount: undefined, + useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + }), }) - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - const shouldBeGoverned = index !== 0 && governance - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - async function getInstruction(): Promise { - const isValid = await validateInstruction({ schema, form, setFormErrors }) - const obj: UiInstruction = { - serializedInstruction: '', - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - }) - return ( - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - ) + return null } export default Empty diff --git a/pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx b/pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx index 3ea1d8a747..625d404ab5 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx @@ -1,9 +1,7 @@ import { useState } from 'react' import { Governance, ProgramAccount } from '@solana/spl-governance' - import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' import { GovernedMultiTypeAccount } from '@utils/tokens' - import { Instructions } from '@utils/uiTypes/proposalCreationTypes' import GovernedAccountSelect from '../GovernedAccountSelect' import SelectedInstruction from './SelectedInstruction' @@ -30,19 +28,10 @@ const ProposalForm = ({ {![ Instructions.Transfer, Instructions.Mint, - Instructions.ProgramUpgrade, - Instructions.SetProgramAuthority, Instructions.Base64, Instructions.Clawback, Instructions.Grant, - Instructions.None, Instructions.FriktionDepositIntoVolt, - Instructions.UXDInitializeController, - Instructions.UXDSetRedeemableGlobalSupplyCap, - Instructions.UXDSetMangoDepositoriesRedeemableSoftCap, - Instructions.UXDRegisterMangoDepository, - Instructions.UXDDepositInsuranceToMangoDepository, - Instructions.UXDWithdrawInsuranceFromMangoDepository, ].includes(itxType) && ( case Instructions.ProgramUpgrade: - return + return case Instructions.SetProgramAuthority: - return + return ( + + ) case Instructions.CreateAssociatedTokenAccount: return ( ) case Instructions.UXDInitializeController: - return + return ( + + ) case Instructions.UXDSetRedeemableGlobalSupplyCap: return ( ) case Instructions.UXDSetMangoDepositoriesRedeemableSoftCap: return ( ) case Instructions.UXDRegisterMangoDepository: - return + return ( + + ) case Instructions.UXDDepositInsuranceToMangoDepository: return ( ) case Instructions.UXDWithdrawInsuranceFromMangoDepository: return ( ) case Instructions.Mint: @@ -140,7 +152,7 @@ const SelectedInstruction = ({ case Instructions.Base64: return case Instructions.None: - return + return case Instructions.Grant: return case Instructions.Clawback: diff --git a/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx b/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx index 64a705321f..b3a2f461be 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SetProgramAuthority.tsx @@ -1,138 +1,66 @@ -import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' +import React from 'react' import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - SetProgramAuthorityForm, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' -import useWalletStore from 'stores/useWalletStore' +import { PublicKey } from '@solana/web3.js' + +import { SetProgramAuthorityForm } from '@utils/uiTypes/proposalCreationTypes' import Input from '@components/inputs/Input' -import { debounce } from '@utils/debounce' -import GovernedAccountSelect from '../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' -import { validateInstruction } from '@utils/instructionTools' import createSetProgramAuthorityInstruction from '@tools/sdk/bpfUpgradeableLoader/createSetProgramAuthorityInstruction' -import { - ProgramAccount, - serializeInstructionToBase64, - Governance, - GovernanceAccountType, -} from '@solana/spl-governance' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' const SetProgramAuthority = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount: GovernedMultiTypeAccount | undefined }) => { - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { getGovernancesByAccountType } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountType( - GovernanceAccountType.ProgramGovernanceV1 - ).map((x) => { - return { - governance: x, - } - }) - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - accountId: programId?.toString(), - destinationAuthority: '', - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - async function getInstruction(): Promise { - const isValid = await validateInstruction({ schema, form, setFormErrors }) - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - wallet?.publicKey - ) { - const upgradeIx = await createSetProgramAuthorityInstruction( - form.governedAccount.governance.account.governedAccount, - form.governedAccount.governance.pubkey, + const { + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + destinationAuthority: yup + .string() + .required('new authority address is required'), + }), + buildInstruction: async function () { + if (!governedAccount?.governance?.account) { + throw new Error('Governance must be a Program Account Governance') + } + if (!form.destinationAuthority) { + throw new Error('missing form input: destination authority') + } + return createSetProgramAuthorityInstruction( + form.governedAccount!.governance.account.governedAccount, + form.governedAccount!.governance.pubkey, new PublicKey(form.destinationAuthority) ) - serializedInstruction = serializeInstructionToBase64(upgradeIx) - } - const obj: UiInstruction = { - serializedInstruction: serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - if (form.destinationAuthority) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.destinationAuthority]) - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - const schema = yup.object().shape({ - destinationAuthority: yup - .string() - .required('new authority address is required'), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), + }, }) return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - - handleSetForm({ - value: evt.target.value, - propertyName: 'destinationAuthority', - }) - } - error={formErrors['destinationAuthority']} - /> - + + handleSetForm({ + value: evt.target.value, + propertyName: 'destinationAuthority', + }) + } + error={formErrors['destinationAuthority']} + /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx index c45a0a3cb4..bdf2e7383c 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx @@ -42,16 +42,14 @@ const DepositReserveLiquidityAndObligationCollateral = ({ .required('Amount is required'), }), buildInstruction: async function () { - if (!form.mintName) - throw new Error('invalid form, missing mintName field') return depositReserveLiquidityAndObligationCollateral({ obligationOwner: governedAccount!.governance.pubkey, liquidityAmount: uiAmountToNativeBN( form.uiAmount, - SolendConfiguration.getSupportedMintInformation(form.mintName) + SolendConfiguration.getSupportedMintInformation(form.mintName!) .decimals ), - mintName: form.mintName, + mintName: form.mintName!, }) }, } diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx index f988018151..6890b9de18 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx @@ -34,11 +34,9 @@ const RefreshObligation = ({ mintName: yup.string().required('Token Name is required'), }), buildInstruction: async function () { - if (!form.mintName) - throw new Error('invalid form, missing mintName field') return refreshObligation({ obligationOwner: governedAccount!.governance.pubkey, - mintNames: [form.mintName], + mintNames: [form.mintName!], }) }, }) diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx index 8227454413..1faa23dfcc 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx @@ -34,10 +34,8 @@ const RefreshReserve = ({ mintName: yup.string().required('Token Name is required'), }), buildInstruction: async function () { - if (!form.mintName) - throw new Error('invalid form, missing mintName field') return refreshReserve({ - mintName: form.mintName, + mintName: form.mintName!, }) }, }) diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx index e8cc0977c4..d21a800fb8 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx @@ -44,17 +44,14 @@ const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({ .required('Amount is required'), }), buildInstruction: async function () { - if (!form.mintName) - throw new Error('invalid form, missing mintName field') - return withdrawObligationCollateralAndRedeemReserveLiquidity({ obligationOwner: governedAccount!.governance.pubkey, liquidityAmount: uiAmountToNativeBN( form.uiAmount, - SolendConfiguration.getSupportedMintInformation(form.mintName) + SolendConfiguration.getSupportedMintInformation(form.mintName!) .decimals ), - mintName: form.mintName, + mintName: form.mintName!, ...(form.destinationLiquidity && { destinationLiquidity: new PublicKey(form.destinationLiquidity), }), diff --git a/pages/dao/[symbol]/proposal/components/instructions/UXD/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/DepositInsuranceToMangoDepository.tsx index 370a3fb310..a2e307d58a 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/UXD/DepositInsuranceToMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/DepositInsuranceToMangoDepository.tsx @@ -1,166 +1,67 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - DepositInsuranceToMangoDepositoryForm, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' -import useWalletStore from 'stores/useWalletStore' import Input from '@components/inputs/Input' -import { debounce } from '@utils/debounce' -import GovernedAccountSelect from '../../GovernedAccountSelect' -import { GovernedMultiTypeAccount } from '@utils/tokens' -import createDepositInsuranceToMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction' import Select from '@components/inputs/Select' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import createDepositInsuranceToMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createDepositInsuranceToMangoDepositoryInstruction' import { getDepositoryMintSymbols, getInsuranceMintSymbols, } from '@tools/sdk/uxdProtocol/uxdClient' -import { - ProgramAccount, - serializeInstructionToBase64, - Governance, - GovernanceAccountType, -} from '@solana/spl-governance' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { DepositInsuranceToMangoDepositoryForm } from '@utils/uiTypes/proposalCreationTypes' +import SelectOptionList from '../../SelectOptionList' -const DepositInsuranceToMangoDepository = ({ +const UXDDepositInsuranceToMangoDepository = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount?: GovernedMultiTypeAccount }) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { getGovernancesByAccountTypes } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountTypes([ - GovernanceAccountType.ProgramGovernanceV1, - GovernanceAccountType.ProgramGovernanceV2, - ]).map((x) => { - return { - governance: x, - } - }) - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - programId: programId?.toString(), - collateralName: '', - insuranceName: '', - insuranceDepositedAmount: 0, - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - async function getInstruction(): Promise { - const isValid = await validateInstruction() - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - wallet?.publicKey - ) { - const createIx = await createDepositInsuranceToMangoDepositoryInstruction( + const { + connection, + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + insuranceDepositedAmount: 0, + }, + schema: yup.object().shape({ + collateralName: yup + .string() + .required('Collateral Name address is required'), + insuranceName: yup + .string() + .required('Insurance Name address is required'), + insuranceDepositedAmount: yup + .number() + .moreThan(0, 'Insurance Deposited amount should be more than 0') + .required('Insurance Deposited amount is required'), + governedAccount: yup + .object() + .nullable() + .required('Governance account is required'), + }), + buildInstruction: async function () { + if (!governedAccount?.governance?.account) { + throw new Error('Governance must be a Program Account Governance') + } + return createDepositInsuranceToMangoDepositoryInstruction( connection, - form.governedAccount?.governance.account.governedAccount, - form.governedAccount?.governance.pubkey, - form.collateralName, - form.insuranceName, + form.governedAccount!.governance!.account.governedAccount, + form.governedAccount!.governance!.pubkey, + form.collateralName!, + form.insuranceName!, form.insuranceDepositedAmount ) - serializedInstruction = serializeInstructionToBase64(createIx) - } - const obj: UiInstruction = { - serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - if (form.collateralName) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.collateralName]) - - useEffect(() => { - if (form.insuranceName) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.insuranceName]) - - useEffect(() => { - if (form.insuranceDepositedAmount) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.insuranceDepositedAmount]) - - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - const schema = yup.object().shape({ - collateralName: yup - .string() - .required('Collateral Name address is required'), - insuranceName: yup.string().required('Insurance Name address is required'), - insuranceDepositedAmount: yup - .number() - .moreThan(0, 'Insurance Deposited amount should be more than 0') - .required('Insurance Deposited amount is required'), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), + }, }) return ( <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > | null + governedAccount?: GovernedMultiTypeAccount }) => { - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { getGovernancesByAccountTypes } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountTypes([ - GovernanceAccountType.ProgramGovernanceV1, - GovernanceAccountType.ProgramGovernanceV2, - ]).map((x) => { - return { - governance: x, - } - }) - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - programId: programId?.toString(), - mintDecimals: 0, - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - async function getInstruction(): Promise { - const isValid = await validateInstruction() - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - form.mintDecimals && - wallet?.publicKey - ) { - const initializeControllerIx = createInitializeControllerInstruction( - form.governedAccount?.governance.account.governedAccount, + const { + wallet, + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + mintDecimals: 0, + }, + schema: yup.object().shape({ + mintDecimals: yup + .number() + .min(0, 'Mint decimals cannot be less than 0') + .max(9, 'Mint decimals cannot be more than 9') + .required('Mint Decimals is required'), + governedAccount: yup + .object() + .nullable() + .required('Governance account is required'), + }), + buildInstruction: async function () { + if (!governedAccount?.governance?.account) { + throw new Error('Governance must be a Program Account Governance') + } + return createInitializeControllerInstruction( + form.governedAccount!.governance.account.governedAccount, form.mintDecimals, - form.governedAccount?.governance.pubkey, - new PublicKey(wallet.publicKey.toBase58()) + form.governedAccount!.governance.pubkey, + new PublicKey(wallet!.publicKey!.toBase58()) ) - serializedInstruction = serializeInstructionToBase64( - initializeControllerIx - ) - } - const obj: UiInstruction = { - serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - const schema = yup.object().shape({ - mintDecimals: yup - .number() - .min(0, 'Mint decimals cannot be less than 0') - .max(9, 'Mint decimals cannot be more than 9') - .required('Mint Decimals is required'), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), + }, }) return ( <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > | null + governedAccount?: GovernedMultiTypeAccount }) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { getGovernancesByAccountTypes } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountTypes([ - GovernanceAccountType.ProgramGovernanceV1, - GovernanceAccountType.ProgramGovernanceV2, - ]).map((x) => { - return { - governance: x, - } - }) - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - programId: programId?.toString(), - collateralName: '', - insuranceName: '', - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - async function getInstruction(): Promise { - const isValid = await validateInstruction() - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - form.insuranceName && - wallet?.publicKey - ) { - const createIx = await createRegisterMangoDepositoryInstruction( + const { + connection, + wallet, + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema: yup.object().shape({ + collateralName: yup + .string() + .required('Valid Collateral name is required'), + insuranceName: yup.string().required('Valid Insurance name is required'), + governedAccount: yup + .object() + .nullable() + .required('Governance account is required'), + }), + buildInstruction: async function () { + if (!governedAccount?.governance?.account) { + throw new Error('Governance must be a Program Account Governance') + } + return createRegisterMangoDepositoryInstruction( connection, - form.governedAccount?.governance.account.governedAccount, - form.governedAccount?.governance.pubkey, - new PublicKey(wallet.publicKey.toBase58()), - form.collateralName, - form.insuranceName + form.governedAccount!.governance.account.governedAccount, + form.governedAccount!.governance.pubkey, + new PublicKey(wallet!.publicKey!.toBase58()), + form.collateralName!, + form.insuranceName! ) - serializedInstruction = serializeInstructionToBase64(createIx) - } - const obj: UiInstruction = { - serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - if (form.collateralName) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.collateralName]) - - useEffect(() => { - if (form.insuranceName) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.insuranceName]) - - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - collateralName: yup.string().required('Valid Collateral name is required'), - insuranceName: yup.string().required('Valid Insurance name is required'), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), + }, }) return ( <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - ) diff --git a/pages/dao/[symbol]/proposal/components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap.tsx index fa97a38a04..6cefdd9687 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -1,151 +1,63 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - SetMangoDepositoriesRedeemableSoftCapForm, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' -import useWalletStore from 'stores/useWalletStore' import Input from '@components/inputs/Input' -import { debounce } from '@utils/debounce' -import GovernedAccountSelect from '../../GovernedAccountSelect' -import { GovernedMultiTypeAccount } from '@utils/tokens' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import createSetMangoDepositoriesRedeemableSoftCapInstruction from '@tools/sdk/uxdProtocol/createSetMangoDepositoriesRedeemableSoftCapInstruction' -import { - ProgramAccount, - serializeInstructionToBase64, - Governance, - GovernanceAccountType, -} from '@solana/spl-governance' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { SetMangoDepositoriesRedeemableSoftCapForm } from '@utils/uiTypes/proposalCreationTypes' const SetMangoDepositoriesRedeemableSoftCap = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount?: GovernedMultiTypeAccount }) => { - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { getGovernancesByAccountTypes } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountTypes([ - GovernanceAccountType.ProgramGovernanceV1, - GovernanceAccountType.ProgramGovernanceV2, - ]).map((x) => { - return { - governance: x, - } - }) - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - programId: programId?.toString(), - softCap: 0, - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - async function getInstruction(): Promise { - const isValid = await validateInstruction() - let serializedInstruction = '' - - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - wallet?.publicKey - ) { - const createIx = createSetMangoDepositoriesRedeemableSoftCapInstruction( - form.governedAccount.governance?.account.governedAccount, + const { + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + softCap: 0, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governance account is required'), + softCap: yup + .number() + .moreThan(0, 'Redeemable soft cap should be more than 0') + .required('Redeemable soft cap is required'), + }), + buildInstruction: async function () { + if (!governedAccount?.governance?.account) { + throw new Error('Governance must be a Program Account Governance') + } + return createSetMangoDepositoriesRedeemableSoftCapInstruction( + form.governedAccount!.governance.account.governedAccount, form.softCap, - form.governedAccount?.governance.pubkey + form.governedAccount!.governance.pubkey ) - serializedInstruction = serializeInstructionToBase64(createIx) - } - const obj: UiInstruction = { - serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - if (form.softCap) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.softCap]) - - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - softCap: yup - .number() - .moreThan(0, 'Redeemable soft cap should be more than 0') - .required('Redeemable soft cap is required'), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), + }, }) return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - - - handleSetForm({ - value: evt.target.value, - propertyName: 'softCap', - }) - } - error={formErrors['softCap']} - /> - + + handleSetForm({ + value: evt.target.value, + propertyName: 'softCap', + }) + } + error={formErrors['softCap']} + /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/UXD/SetRedeemGlobalSupplyCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetRedeemGlobalSupplyCap.tsx index a6b0a4cd42..abe364ddc4 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/UXD/SetRedeemGlobalSupplyCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetRedeemGlobalSupplyCap.tsx @@ -1,150 +1,64 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - SetRedeemableGlobalSupplyCapForm, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' -import useWalletStore from 'stores/useWalletStore' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import createSetRedeemableGlobalSupplyCapInstruction from '@tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction' +import { SetRedeemableGlobalSupplyCapForm } from '@utils/uiTypes/proposalCreationTypes' import Input from '@components/inputs/Input' -import { debounce } from '@utils/debounce' -import GovernedAccountSelect from '../../GovernedAccountSelect' import { GovernedMultiTypeAccount } from '@utils/tokens' -import createSetRedeemableGlobalSupplyCapInstruction from '@tools/sdk/uxdProtocol/createSetRedeemableGlobalSupplyCapInstruction' -import { - ProgramAccount, - serializeInstructionToBase64, - Governance, - GovernanceAccountType, -} from '@solana/spl-governance' const SetRedeemGlobalSupplyCap = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount?: GovernedMultiTypeAccount }) => { - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { getGovernancesByAccountTypes } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountTypes([ - GovernanceAccountType.ProgramGovernanceV1, - GovernanceAccountType.ProgramGovernanceV2, - ]).map((x) => { - return { - governance: x, - } - }) - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - programId: programId?.toString(), - supplyCap: 0, - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - async function getInstruction(): Promise { - const isValid = await validateInstruction() - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - wallet?.publicKey - ) { - const createIx = createSetRedeemableGlobalSupplyCapInstruction( - form.governedAccount.governance?.account.governedAccount, + const { + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + supplyCap: 0, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + supplyCap: yup + .number() + .moreThan(0, 'Redeemable global supply cap should be more than 0') + .required('Redeemable global supply cap is required'), + }), + buildInstruction: async function () { + if (!governedAccount?.governance?.account) { + throw new Error('Governance must be a Program Account Governance') + } + return createSetRedeemableGlobalSupplyCapInstruction( + form.governedAccount!.governance.account.governedAccount, form.supplyCap, - form.governedAccount?.governance.pubkey + form.governedAccount!.governance.pubkey ) - serializedInstruction = serializeInstructionToBase64(createIx) - } - const obj: UiInstruction = { - serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - if (form.supplyCap) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.supplyCap]) - - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - supplyCap: yup - .number() - .moreThan(0, 'Redeemable global supply cap should be more than 0') - .required('Redeemable global supply cap is required'), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), + }, }) return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - - - handleSetForm({ - value: evt.target.value, - propertyName: 'supplyCap', - }) - } - error={formErrors['supplyCap']} - /> - + + handleSetForm({ + value: evt.target.value, + propertyName: 'supplyCap', + }) + } + error={formErrors['supplyCap']} + /> ) } diff --git a/pages/dao/[symbol]/proposal/components/instructions/UXD/WithdrawInsuranceFromMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/WithdrawInsuranceFromMangoDepository.tsx index 915e2b396a..16911b8c09 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/UXD/WithdrawInsuranceFromMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/WithdrawInsuranceFromMangoDepository.tsx @@ -1,166 +1,63 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React, { useContext, useEffect, useState } from 'react' -import useRealm from '@hooks/useRealm' -import { PublicKey } from '@solana/web3.js' import * as yup from 'yup' -import { isFormValid } from '@utils/formValidation' -import { - UiInstruction, - WithdrawInsuranceFromMangoDepositoryForm, -} from '@utils/uiTypes/proposalCreationTypes' -import { NewProposalContext } from '../../../new' -import useGovernanceAssets from '@hooks/useGovernanceAssets' -import useWalletStore from 'stores/useWalletStore' -import { - serializeInstructionToBase64, - Governance, - GovernanceAccountType, - ProgramAccount, -} from '@solana/spl-governance' import Input from '@components/inputs/Input' -import { debounce } from '@utils/debounce' -import GovernedAccountSelect from '../../GovernedAccountSelect' -import { GovernedMultiTypeAccount } from '@utils/tokens' -import createWithdrawInsuranceFromMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction' import Select from '@components/inputs/Select' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import createWithdrawInsuranceFromMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createWithdrawInsuranceFromMangoDepositoryInstruction' import { getDepositoryMintSymbols, getInsuranceMintSymbols, } from '@tools/sdk/uxdProtocol/uxdClient' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { WithdrawInsuranceFromMangoDepositoryForm } from '@utils/uiTypes/proposalCreationTypes' +import SelectOptionList from '../../SelectOptionList' const WithdrawInsuranceFromMangoDepository = ({ index, - governance, + governedAccount, }: { index: number - governance: ProgramAccount | null + governedAccount?: GovernedMultiTypeAccount }) => { - const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { getGovernancesByAccountTypes } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountTypes([ - GovernanceAccountType.ProgramGovernanceV1, - GovernanceAccountType.ProgramGovernanceV2, - ]).map((x) => { - return { - governance: x, - } - }) - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - programId: programId?.toString(), - collateralName: '', - insuranceName: '', - insuranceWithdrawnAmount: 0, - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - const validateInstruction = async (): Promise => { - const { isValid, validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - return isValid - } - async function getInstruction(): Promise { - const isValid = await validateInstruction() - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - wallet?.publicKey - ) { - const createIx = await createWithdrawInsuranceFromMangoDepositoryInstruction( + const { + connection, + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + insuranceWithdrawnAmount: 0, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + collateralName: yup.string().required('Collateral Name is required'), + insuranceName: yup.string().required('Insurance Name is required'), + insuranceWithdrawnAmount: yup + .number() + .moreThan(0, 'Insurance Withdrawn amount should be more than 0') + .required('Insurance Withdrawn amount is required'), + }), + buildInstruction: async function () { + if (!governedAccount?.governance?.account) { + throw new Error('Governance must be a Program Account Governance') + } + return createWithdrawInsuranceFromMangoDepositoryInstruction( connection, - form.governedAccount?.governance.account.governedAccount, - form.governedAccount?.governance.pubkey, - form.collateralName, - form.insuranceName, + form.governedAccount!.governance.account.governedAccount, + form.governedAccount!.governance.pubkey, + form.collateralName!, + form.insuranceName!, form.insuranceWithdrawnAmount ) - serializedInstruction = serializeInstructionToBase64(createIx) - } - const obj: UiInstruction = { - serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - useEffect(() => { - handleSetForm({ - propertyName: 'programId', - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - if (form.collateralName) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.collateralName]) - - useEffect(() => { - if (form.insuranceName) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.insuranceName]) - - useEffect(() => { - if (form.insuranceWithdrawnAmount) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.insuranceWithdrawnAmount]) - - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - collateralName: yup.string().required('Collateral Name is required'), - insuranceName: yup.string().required('Insurance Name is required'), - insuranceWithdrawnAmount: yup - .number() - .moreThan(0, 'Insurance Withdrawn amount should be more than 0') - .required('Insurance Withdrawn amount is required'), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), + }, }) return ( <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > - | null + governedAccount: GovernedMultiTypeAccount | undefined }) => { const connection = useWalletStore((s) => s.connection) - const wallet = useWalletStore((s) => s.current) - const { realmInfo } = useRealm() - const { getGovernancesByAccountTypes } = useGovernanceAssets() - const governedProgramAccounts = getGovernancesByAccountTypes([ - GovernanceAccountType.ProgramGovernanceV1, - GovernanceAccountType.ProgramGovernanceV2, - ]).map((x) => { - return { - governance: x, - } - }) - const shouldBeGoverned = index !== 0 && governance - const programId: PublicKey | undefined = realmInfo?.programId - const [form, setForm] = useState({ - governedAccount: undefined, - programId: programId?.toString(), - bufferAddress: '', - bufferSpillAddress: wallet?.publicKey?.toBase58(), - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - async function getInstruction(): Promise { - const isValid = await validateInstruction({ schema, form, setFormErrors }) - let serializedInstruction = '' - if ( - isValid && - programId && - form.governedAccount?.governance?.account && - wallet?.publicKey - ) { - const bufferSpillAddress = form.bufferSpillAddress - ? new PublicKey(form.bufferSpillAddress) - : wallet.publicKey - - const upgradeIx = await createUpgradeInstruction( - form.governedAccount.governance.account.governedAccount, - new PublicKey(form.bufferAddress), - form.governedAccount.governance.pubkey, - bufferSpillAddress - ) - serializedInstruction = serializeInstructionToBase64(upgradeIx) - } - const obj: UiInstruction = { - serializedInstruction: serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - } - return obj - } - useEffect(() => { - handleSetForm({ - propertyName: programUpgradeFormNameOf('programId'), - value: programId?.toString(), - }) - }, [realmInfo?.programId]) - - useEffect(() => { - handleSetForm({ - propertyName: programUpgradeFormNameOf('bufferSpillAddress'), - value: wallet?.publicKey?.toBase58(), - }) - }, [wallet?.publicKey?.toBase58()]) - - useEffect(() => { - if (form.bufferAddress) { - debounce.debounceFcn(async () => { - const { validationErrors } = await isFormValid(schema, form) - setFormErrors(validationErrors) - }) - } - }, [form.bufferAddress]) - - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - - const schema = yup.object().shape({ - bufferAddress: yup - .string() - .test('bufferTest', 'Invalid buffer', async function (val: string) { - if (val) { - try { - await validateBuffer( - connection, - val, - form.governedAccount?.governance?.pubkey - ) - return true - } catch (e) { - return this.createError({ - message: `${e}`, - }) - } - } else { - return this.createError({ - message: `Buffer address is required`, - }) - } - }), - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), - - bufferSpillAddress: yup - .string() - .test( - 'bufferSpillAddressTest', - 'Invalid buffer spill address', - async function (val: string) { + const { + wallet, + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema: yup.object().shape({ + bufferAddress: yup + .string() + .test('bufferTest', 'Invalid buffer', async function (val: string) { if (val) { try { - await validateAccount(connection, val) + await validateBuffer( + connection, + val, + form.governedAccount?.governance?.pubkey + ) return true - } catch (ex) { + } catch (e) { return this.createError({ - message: `${ex}`, + message: `${e}`, }) } - return true } else { return this.createError({ - message: `Buffer spill address is required`, + message: `Buffer address is required`, }) } - } - ), + }), + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + + bufferSpillAddress: yup + .string() + .test( + 'bufferSpillAddressTest', + 'Invalid buffer spill address', + async function (val: string) { + if (val) { + try { + await validateAccount(connection, val) + return true + } catch (ex) { + return this.createError({ + message: `${ex}`, + }) + } + return true + } else { + return this.createError({ + message: `Buffer spill address is required`, + }) + } + } + ), + }), + buildInstruction: async function () { + if (!governedAccount?.governance?.account) { + throw new Error('Governance must be a Program Account Governance') + } + + const bufferSpillAddress = form.bufferSpillAddress + ? new PublicKey(form.bufferSpillAddress) + : wallet!.publicKey! + + return createUpgradeInstruction( + form.governedAccount!.governance.account.governedAccount, + new PublicKey(form.bufferAddress!), + form.governedAccount!.governance.pubkey, + bufferSpillAddress + ) + }, }) return ( <> - { - handleSetForm({ - value, - propertyName: programUpgradeFormNameOf('governedAccount'), - }) - }} - value={form.governedAccount} - error={formErrors[programUpgradeFormNameOf('governedAccount')]} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - > () export interface SetProgramAuthorityForm { - governedAccount: GovernedProgramAccount | GovernedTokenAccount | undefined - accountId: string | undefined - destinationAuthority: string + governedAccount: GovernedMultiTypeAccount | undefined + destinationAuthority?: string } export interface Base64InstructionForm { governedAccount?: GovernedMultiTypeAccount @@ -155,44 +152,38 @@ export interface RemoveLiquidityRaydiumForm { } export interface InitializeControllerForm { - governedAccount: GovernedProgramAccount | undefined + governedAccount: GovernedMultiTypeAccount | undefined mintDecimals: number - programId: string | undefined } export interface SetRedeemableGlobalSupplyCapForm { - governedAccount: GovernedProgramAccount | undefined + governedAccount: GovernedMultiTypeAccount | undefined supplyCap: number - programId: string | undefined } export interface SetMangoDepositoriesRedeemableSoftCapForm { - governedAccount: GovernedProgramAccount | undefined + governedAccount: GovernedMultiTypeAccount | undefined softCap: number - programId: string | undefined } export interface RegisterMangoDepositoryForm { - governedAccount: GovernedProgramAccount | undefined - collateralName: string - insuranceName: string - programId: string | undefined + governedAccount: GovernedMultiTypeAccount | undefined + collateralName?: string + insuranceName?: string } export interface DepositInsuranceToMangoDepositoryForm { - governedAccount: GovernedProgramAccount | undefined - collateralName: string - insuranceName: string + governedAccount: GovernedMultiTypeAccount | undefined + collateralName?: string + insuranceName?: string insuranceDepositedAmount: number - programId: string | undefined } export interface WithdrawInsuranceFromMangoDepositoryForm { - governedAccount: GovernedProgramAccount | undefined - collateralName: string - insuranceName: string + governedAccount: GovernedMultiTypeAccount | undefined + collateralName?: string + insuranceName?: string insuranceWithdrawnAmount: number - programId: string | undefined } export enum Instructions { From d85167ece648b21ae1eef552c1998f2a054604ef Mon Sep 17 00:00:00 2001 From: GregoryNEUT Date: Wed, 30 Mar 2022 11:19:24 +0200 Subject: [PATCH 119/204] add HotWallet component + TokenAccounts plugin + Tribeca Gauges plugin + Saber Stats plugin (#48) * add HotWallet component + TokenAccounts plugin * Change ui * add saber tribeca plugin * add TribecaGauges + SaberStats plugins * replace moment by native Date * Fixed dependencies * Implement thomas comments --- components/HotWallet/HotWallet.tsx | 44 + components/HotWallet/HotWalletName.tsx | 41 + .../plugins/SaberStats/SaberStat.tsx | 30 + .../plugins/SaberStats/SaberStats.tsx | 33 + .../plugins/TokenAccounts/TokenAccount.tsx | 56 + .../plugins/TokenAccounts/TokenAccounts.tsx | 39 + .../TribecaGauges/ActiveGaugeVotes.tsx | 37 + .../TribecaGauges/EpochGaugeVoterData.tsx | 37 + .../plugins/TribecaGauges/EscrowData.tsx | 81 + .../plugins/TribecaGauges/TribecaGauges.tsx | 50 + .../TribecaGauges/TribecaGaugesEpoch.tsx | 68 + .../instructions/programs/uxdProtocol.tsx | 30 +- hooks/useHotWallet.ts | 43 + hooks/useHotWalletPluginTokenAccounts.ts | 112 + hooks/useSaberStats.ts | 117 + hooks/useTribecaGauge.ts | 30 + hooks/useTribecaGaugesInfos.ts | 213 ++ hooks/useTribecaLockerData.ts | 31 + hooks/useTribecaPrograms.ts | 42 + package.json | 8 +- pages/dao/[symbol]/index.tsx | 7 + tools/sdk/quarryMine/configuration.ts | 61 + tools/sdk/tribeca/ATribecaConfiguration.ts | 288 ++ tools/sdk/tribeca/configurations.ts | 39 + tools/sdk/tribeca/idls/gauge.ts | 2471 +++++++++++++++++ tools/sdk/tribeca/idls/govern.ts | 1638 +++++++++++ tools/sdk/tribeca/idls/locked_voter.ts | 1399 ++++++++++ tools/sdk/tribeca/idls/quarry_mine.ts | 1963 +++++++++++++ tools/sdk/tribeca/programs/gauge.ts | 30 + tools/sdk/tribeca/programs/govern.ts | 37 + tools/sdk/tribeca/programs/index.ts | 4 + tools/sdk/tribeca/programs/lockedVoter.ts | 22 + tools/sdk/tribeca/programs/mine.ts | 28 + tools/sdk/units.ts | 10 + utils/splTokens.ts | 58 + yarn.lock | 66 + 36 files changed, 9245 insertions(+), 18 deletions(-) create mode 100644 components/HotWallet/HotWallet.tsx create mode 100644 components/HotWallet/HotWalletName.tsx create mode 100644 components/HotWallet/plugins/SaberStats/SaberStat.tsx create mode 100644 components/HotWallet/plugins/SaberStats/SaberStats.tsx create mode 100644 components/HotWallet/plugins/TokenAccounts/TokenAccount.tsx create mode 100644 components/HotWallet/plugins/TokenAccounts/TokenAccounts.tsx create mode 100644 components/HotWallet/plugins/TribecaGauges/ActiveGaugeVotes.tsx create mode 100644 components/HotWallet/plugins/TribecaGauges/EpochGaugeVoterData.tsx create mode 100644 components/HotWallet/plugins/TribecaGauges/EscrowData.tsx create mode 100644 components/HotWallet/plugins/TribecaGauges/TribecaGauges.tsx create mode 100644 components/HotWallet/plugins/TribecaGauges/TribecaGaugesEpoch.tsx create mode 100644 hooks/useHotWallet.ts create mode 100644 hooks/useHotWalletPluginTokenAccounts.ts create mode 100644 hooks/useSaberStats.ts create mode 100644 hooks/useTribecaGauge.ts create mode 100644 hooks/useTribecaGaugesInfos.ts create mode 100644 hooks/useTribecaLockerData.ts create mode 100644 hooks/useTribecaPrograms.ts create mode 100644 tools/sdk/quarryMine/configuration.ts create mode 100644 tools/sdk/tribeca/ATribecaConfiguration.ts create mode 100644 tools/sdk/tribeca/configurations.ts create mode 100644 tools/sdk/tribeca/idls/gauge.ts create mode 100644 tools/sdk/tribeca/idls/govern.ts create mode 100644 tools/sdk/tribeca/idls/locked_voter.ts create mode 100644 tools/sdk/tribeca/idls/quarry_mine.ts create mode 100644 tools/sdk/tribeca/programs/gauge.ts create mode 100644 tools/sdk/tribeca/programs/govern.ts create mode 100644 tools/sdk/tribeca/programs/index.ts create mode 100644 tools/sdk/tribeca/programs/lockedVoter.ts create mode 100644 tools/sdk/tribeca/programs/mine.ts diff --git a/components/HotWallet/HotWallet.tsx b/components/HotWallet/HotWallet.tsx new file mode 100644 index 0000000000..201023bafa --- /dev/null +++ b/components/HotWallet/HotWallet.tsx @@ -0,0 +1,44 @@ +import useHotWallet from '@hooks/useHotWallet' +import { FireIcon } from '@heroicons/react/solid' +import HotWalletName from './HotWalletName' +import HotWalletPluginTribecaGauges from './plugins/TribecaGauges/TribecaGauges' +import { saberTribecaConfiguration } from '@tools/sdk/tribeca/configurations' +import HotWalletPluginTokenAccounts from './plugins/TokenAccounts/TokenAccounts' +import HotWalletPluginSaberStats from './plugins/SaberStats/SaberStats' + +const HotWallet = (): JSX.Element => { + const { hotWalletAccount } = useHotWallet() + + if (!hotWalletAccount) { + return <> + } + + return ( +
+

+ + Hot Wallet +

+ + + +
+ + + + + +
+
+ ) +} + +export default HotWallet diff --git a/components/HotWallet/HotWalletName.tsx b/components/HotWallet/HotWalletName.tsx new file mode 100644 index 0000000000..818bb4bc53 --- /dev/null +++ b/components/HotWallet/HotWalletName.tsx @@ -0,0 +1,41 @@ +import { getExplorerUrl } from '@components/explorer/tools' +import { ExternalLinkIcon } from '@heroicons/react/outline' +import { PublicKey } from '@solana/web3.js' +import { abbreviateAddress } from '@utils/formatting' +import { createRef } from 'react' +import useWalletStore from 'stores/useWalletStore' + +const HotWalletName = ({ + hotWalletName, + hotWalletAddress, +}: { + hotWalletName: string + hotWalletAddress: PublicKey +}) => { + const connection = useWalletStore((store) => store.connection) + + const linkRef = createRef() + + return ( +
linkRef.current?.click()} + > +

{hotWalletName}

+

{abbreviateAddress(hotWalletAddress)}

+ + e.stopPropagation()} + > + + +
+ ) +} + +export default HotWalletName diff --git a/components/HotWallet/plugins/SaberStats/SaberStat.tsx b/components/HotWallet/plugins/SaberStats/SaberStat.tsx new file mode 100644 index 0000000000..56298e5283 --- /dev/null +++ b/components/HotWallet/plugins/SaberStats/SaberStat.tsx @@ -0,0 +1,30 @@ +import { SaberStats } from '@hooks/useSaberStats' + +const SaberStat = ({ + liquidityPoolName, + uiBalance, + pendingRewards, + mintName, +}: SaberStats) => { + return ( +
+ {liquidityPoolName} +
+ Balance + {`${uiBalance.toLocaleString()} ${mintName}`} +
+ +
+ Pending Rewards + {pendingRewards.map(({ name, uiPendingAmount }) => ( + {`${uiPendingAmount.toLocaleString()} ${name}`} + ))} +
+
+ ) +} + +export default SaberStat diff --git a/components/HotWallet/plugins/SaberStats/SaberStats.tsx b/components/HotWallet/plugins/SaberStats/SaberStats.tsx new file mode 100644 index 0000000000..135f3eefd0 --- /dev/null +++ b/components/HotWallet/plugins/SaberStats/SaberStats.tsx @@ -0,0 +1,33 @@ +import useSaberStats from '@hooks/useSaberStats' +import { SupportIcon } from '@heroicons/react/outline' +import SaberStat from './SaberStat' +import { HotWalletAccount } from '@hooks/useHotWallet' + +const HotWalletPluginSaberStats = ({ + hotWalletAccount, +}: { + hotWalletAccount: HotWalletAccount +}) => { + const { saberStats } = useSaberStats(hotWalletAccount) + + if (!saberStats) { + return <> + } + + return ( +
+

+ + Saber Stats +

+ +
+ {saberStats.map((saberStat) => ( + + ))} +
+
+ ) +} + +export default HotWalletPluginSaberStats diff --git a/components/HotWallet/plugins/TokenAccounts/TokenAccount.tsx b/components/HotWallet/plugins/TokenAccounts/TokenAccount.tsx new file mode 100644 index 0000000000..d932788a58 --- /dev/null +++ b/components/HotWallet/plugins/TokenAccounts/TokenAccount.tsx @@ -0,0 +1,56 @@ +import { abbreviateAddress } from '@utils/formatting' +import { HotWalletTokenAccounts } from '@hooks/useHotWalletPluginTokenAccounts' +import { getExplorerUrl } from '@components/explorer/tools' +import useWalletStore from 'stores/useWalletStore' +import { createRef } from 'react' +import { ExternalLinkIcon } from '@heroicons/react/outline' +import { nativeAmountToFormattedUiAmount } from '@tools/sdk/units' + +const TokenAccount = ({ info }: { info: HotWalletTokenAccounts[0] }) => { + const connection = useWalletStore((store) => store.connection) + + const linkRef = createRef() + + const amountFormatted = nativeAmountToFormattedUiAmount( + info.amount, + info.decimals + ) + + const usdTotalValueFormatted = info.usdTotalValue.isZero() + ? '' + : `$${nativeAmountToFormattedUiAmount(info.usdTotalValue, info.decimals)}` + + return ( +
linkRef.current?.click()} + > +
+ + {amountFormatted} + + {info.mintName ?? abbreviateAddress(info.mint)} + + + + {usdTotalValueFormatted} +
+ + e.stopPropagation()} + > + + {abbreviateAddress(info.publicKey)} + + + +
+ ) +} + +export default TokenAccount diff --git a/components/HotWallet/plugins/TokenAccounts/TokenAccounts.tsx b/components/HotWallet/plugins/TokenAccounts/TokenAccounts.tsx new file mode 100644 index 0000000000..62ada9fcd3 --- /dev/null +++ b/components/HotWallet/plugins/TokenAccounts/TokenAccounts.tsx @@ -0,0 +1,39 @@ +import { BookOpenIcon } from '@heroicons/react/solid' +import { HotWalletAccount } from '@hooks/useHotWallet' +import useHotWalletPluginTokenAccounts from '@hooks/useHotWalletPluginTokenAccounts' +import TokenAccount from './TokenAccount' + +const HotWalletPluginTokenAccounts = ({ + hotWalletAccount, +}: { + hotWalletAccount: HotWalletAccount +}) => { + const { tokenAccounts } = useHotWalletPluginTokenAccounts(hotWalletAccount) + + if (!hotWalletAccount) { + return <> + } + + return ( +
+

+ + Token Accounts ({tokenAccounts?.length}) +

+ +
+ {tokenAccounts?.map((tokenAccount) => ( + + ))} +
+
+ ) +} + +export default HotWalletPluginTokenAccounts diff --git a/components/HotWallet/plugins/TribecaGauges/ActiveGaugeVotes.tsx b/components/HotWallet/plugins/TribecaGauges/ActiveGaugeVotes.tsx new file mode 100644 index 0000000000..be67054660 --- /dev/null +++ b/components/HotWallet/plugins/TribecaGauges/ActiveGaugeVotes.tsx @@ -0,0 +1,37 @@ +import { ActiveGaugeVoteData } from '@hooks/useTribecaGaugesInfos' + +const ActiveGaugeVotes = ({ + activeGaugeVotesData, +}: { + activeGaugeVotesData?: ActiveGaugeVoteData[] | null +}) => { + return ( +
+

Vote Weight

+ + {activeGaugeVotesData && activeGaugeVotesData.length > 0 ? ( + activeGaugeVotesData.map( + ({ name, logoURI, weight, weightPercentage }) => ( +
+ {logoURI && } + + {name} + + + {weight}{' '} + ({weightPercentage}%) + +
+ ) + ) + ) : ( +
No weight repartition
+ )} +
+ ) +} + +export default ActiveGaugeVotes diff --git a/components/HotWallet/plugins/TribecaGauges/EpochGaugeVoterData.tsx b/components/HotWallet/plugins/TribecaGauges/EpochGaugeVoterData.tsx new file mode 100644 index 0000000000..faeb2925ed --- /dev/null +++ b/components/HotWallet/plugins/TribecaGauges/EpochGaugeVoterData.tsx @@ -0,0 +1,37 @@ +import type { EpochGaugeVoterData } from '@tools/sdk/tribeca/programs' + +const EpochGaugeVoterDataBloc = ({ + title, + epochGaugeVoterData, +}: { + title: string + epochGaugeVoterData?: EpochGaugeVoterData | null +}) => { + return ( +
+

{title}

+ + {epochGaugeVoterData ? ( +
+ + Voting Power{' '} + + {epochGaugeVoterData.votingPower.toNumber().toLocaleString()} + + + + + Allocated Power{' '} + + {epochGaugeVoterData.allocatedPower.toNumber().toLocaleString()} + + +
+ ) : ( +
Non-initialized epoch
+ )} +
+ ) +} + +export default EpochGaugeVoterDataBloc diff --git a/components/HotWallet/plugins/TribecaGauges/EscrowData.tsx b/components/HotWallet/plugins/TribecaGauges/EscrowData.tsx new file mode 100644 index 0000000000..6d12eb2c12 --- /dev/null +++ b/components/HotWallet/plugins/TribecaGauges/EscrowData.tsx @@ -0,0 +1,81 @@ +import { BN } from '@project-serum/anchor' +import type { EscrowData } from '@tools/sdk/tribeca/programs' +import { nativeAmountToFormattedUiAmount } from '@tools/sdk/units' +import { tryGetTokenMint } from '@utils/tokens' +import { useCallback, useEffect, useState } from 'react' +import useWalletStore from 'stores/useWalletStore' + +function formatDate(dateInSec: BN): string { + if (dateInSec.isZero()) { + return '-' + } + + // mul by 1000 to get ms + return new Date(dateInSec.mul(new BN(1000)).toNumber()).toUTCString() +} + +const EscrowDataBloc = ({ escrowData }: { escrowData?: EscrowData }) => { + const connection = useWalletStore((s) => s.connection) + const [uiAmount, setUiAmount] = useState('-') + + const loadUiAmount = useCallback(async (): Promise => { + if (!escrowData) return '-' + + const tokenInfo = await tryGetTokenMint( + connection.current, + escrowData.tokens + ) + + if (!tokenInfo) { + console.log( + 'Cannot load information about token mint related to escrow data (tribeca gauges)', + escrowData.tokens + ) + return '-' + } + + return nativeAmountToFormattedUiAmount( + escrowData.amount, + tokenInfo.account.decimals + ) + }, [connection, escrowData]) + + useEffect(() => { + loadUiAmount().then(setUiAmount) + }, [loadUiAmount]) + + return ( +
+

Escrow Data

+ + {escrowData ? ( +
+ + + Number of Locked Tokens + {' '} + {uiAmount} + + + + Lock Date{' '} + + {formatDate(escrowData.escrowStartedAt)} + + + + + Unlocking Date{' '} + + {formatDate(escrowData.escrowEndsAt)} + + +
+ ) : ( + - + )} +
+ ) +} + +export default EscrowDataBloc diff --git a/components/HotWallet/plugins/TribecaGauges/TribecaGauges.tsx b/components/HotWallet/plugins/TribecaGauges/TribecaGauges.tsx new file mode 100644 index 0000000000..470091e4a1 --- /dev/null +++ b/components/HotWallet/plugins/TribecaGauges/TribecaGauges.tsx @@ -0,0 +1,50 @@ +import { AdjustmentsIcon } from '@heroicons/react/solid' +import useTribecaGaugeInfos from '@hooks/useTribecaGaugesInfos' +import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' +import ActiveGaugeVotes from './ActiveGaugeVotes' +import EpochGaugeVoterData from './EpochGaugeVoterData' +import EscrowData from './EscrowData' +import TribecaGaugesEpoch from './TribecaGaugesEpoch' + +const HotWalletPluginTribecaGauges = ({ + tribecaConfiguration, +}: { + tribecaConfiguration: ATribecaConfiguration +}) => { + const { infos, escrowOwner } = useTribecaGaugeInfos(tribecaConfiguration) + + if (!escrowOwner) { + return <> + } + + return ( +
+

+ + {tribecaConfiguration.name} Tribeca Gauges +

+ + + + + + + + + + +
+ ) +} + +export default HotWalletPluginTribecaGauges diff --git a/components/HotWallet/plugins/TribecaGauges/TribecaGaugesEpoch.tsx b/components/HotWallet/plugins/TribecaGauges/TribecaGaugesEpoch.tsx new file mode 100644 index 0000000000..3c2ed8c1d5 --- /dev/null +++ b/components/HotWallet/plugins/TribecaGauges/TribecaGaugesEpoch.tsx @@ -0,0 +1,68 @@ +import { BN } from '@project-serum/anchor' +import { useEffect, useState } from 'react' + +const ONE_DAY_IN_SEC = 3600 * 24 +const ONE_HOUR_IN_SEC = 3600 +const ONE_MIN_IN_SEC = 60 + +function formatNbSec(nbSec: BN) { + const nbDay = nbSec.div(new BN(ONE_DAY_IN_SEC)) + nbSec = nbSec.sub(nbDay.mul(new BN(ONE_DAY_IN_SEC))) + + const nbHour = nbSec.div(new BN(ONE_HOUR_IN_SEC)) + nbSec = nbSec.sub(nbHour.mul(new BN(ONE_HOUR_IN_SEC))) + + const nbMin = nbSec.div(new BN(ONE_MIN_IN_SEC)) + nbSec = nbSec.sub(nbMin.mul(new BN(ONE_MIN_IN_SEC))) + + return `${nbDay} days ${nbHour} hours ${nbMin} minutes ${nbSec} seconds` +} + +const TribecaGaugesEpoch = ({ + nextEpoch, + rewardsEpoch, + epochDurationSeconds, +}: { + nextEpoch?: BN + rewardsEpoch?: number + epochDurationSeconds?: number +}) => { + const [time, setTime] = useState('-') + const [formattedEpochDuration, setEpochDuration] = useState('-') + + useEffect(() => { + if (!nextEpoch) { + setTime('-') + return + } + + const descriptor = setInterval(() => { + setTime(formatNbSec(nextEpoch.sub(new BN(Date.now()).div(new BN(1000))))) + }, 500) + + return () => { + clearInterval(descriptor) + } + }, [nextEpoch]) + + useEffect(() => { + setEpochDuration( + epochDurationSeconds ? formatNbSec(new BN(epochDurationSeconds)) : '-' + ) + }, [epochDurationSeconds]) + + return ( +
+

Current Epoch

+

{rewardsEpoch}

+ +

Epoch Duration

+

{formattedEpochDuration}

+ +

Next Epoch

+

{time}

+
+ ) +} + +export default TribecaGaugesEpoch diff --git a/components/instructions/programs/uxdProtocol.tsx b/components/instructions/programs/uxdProtocol.tsx index 0203d6fa03..a44e73bd06 100644 --- a/components/instructions/programs/uxdProtocol.tsx +++ b/components/instructions/programs/uxdProtocol.tsx @@ -2,9 +2,10 @@ import { Connection } from '@solana/web3.js' import { struct, u8, u48 } from 'buffer-layout' import { AccountMetaData } from '@solana/spl-governance' import { u128, u64 } from '@project-serum/borsh' -import BigNumber from 'bignumber.js' import { INSURANCE_MINTS } from '@tools/sdk/uxdProtocol/uxdClient' import { UXD_DECIMALS } from '@uxdprotocol/uxd-client' +import { nativeAmountToFormattedUiAmount } from '@tools/sdk/units' +import { BN } from '@project-serum/anchor' export const UXD_PROGRAM_INSTRUCTIONS = { UXD8m9cvwk4RcSxnX2HZ9VudQCEeDH6fRnB4CAP57Dr: { @@ -72,11 +73,10 @@ export const UXD_PROGRAM_INSTRUCTIONS = { const args = dataLayout.decode(Buffer.from(data)) as any return ( <> -

{`Redeemable Global Supply Cap: ${new BigNumber( - args.redeemableGlobalSupplyCap.toString() - ) - .shiftedBy(-UXD_DECIMALS) - .toFormat()}`}

+

{`Redeemable Global Supply Cap: ${nativeAmountToFormattedUiAmount( + new BN(args.redeemableGlobalSupplyCap.toString()), + UXD_DECIMALS + )}`}

) }, @@ -104,11 +104,10 @@ export const UXD_PROGRAM_INSTRUCTIONS = { console.log('args', args) return ( <> -

{`Redeemable Supply Soft Cap: ${new BigNumber( - args.softCap.toString() - ) - .shiftedBy(-UXD_DECIMALS) - .toFormat()}`}

+

{`Redeemable Supply Soft Cap: ${nativeAmountToFormattedUiAmount( + new BN(args.softCap.toString()), + UXD_DECIMALS + )}`}

) }, @@ -203,11 +202,10 @@ export const UXD_PROGRAM_INSTRUCTIONS = { console.log('args', args) return ( <> -

{`Insurance Amount to deposit: ${new BigNumber( - args.insuranceAmount.toString() - ) - .shiftedBy(-INSURANCE_MINTS.mainnet.USDC.decimals) - .toFormat()}`}

+

{`Insurance Amount to deposit: ${nativeAmountToFormattedUiAmount( + new BN(args.insuranceAmount.toString()), + INSURANCE_MINTS.mainnet.USDC.decimals + )}`}

) }, diff --git a/hooks/useHotWallet.ts b/hooks/useHotWallet.ts new file mode 100644 index 0000000000..7b361bdee7 --- /dev/null +++ b/hooks/useHotWallet.ts @@ -0,0 +1,43 @@ +import { PublicKey } from '@solana/web3.js' +import { useEffect, useState } from 'react' +import useRealm from './useRealm' + +const RealmsHotWallet = { + UXDProtocol: { + name: `SOL Treasury's Owner`, + publicKey: new PublicKey('7M6TSEkRiXiYmpRCcCDSdJGTGxAPem2HBqjW4gLQ2KoE'), + }, + + 'Kek World': { + name: `SOL Treasury's Owner`, + publicKey: new PublicKey('AuQHcJZhTd1dnXRrM78RomFiCvW6a9CqxxJ94Fp9h8b'), + }, + + // <---- declare your realm hot wallet here +} + +export type HotWalletAccount = { + name: string + publicKey: PublicKey +} + +const useHotWallet = () => { + const { realm } = useRealm() + + const [hotWalletAccount, setHotWalletAccount] = useState<{ + name: string + publicKey: PublicKey + } | null>(null) + + useEffect(() => { + if (!realm) return + + setHotWalletAccount(RealmsHotWallet[realm.account.name] ?? null) + }, [realm]) + + return { + hotWalletAccount, + } +} + +export default useHotWallet diff --git a/hooks/useHotWalletPluginTokenAccounts.ts b/hooks/useHotWalletPluginTokenAccounts.ts new file mode 100644 index 0000000000..25ee1aee73 --- /dev/null +++ b/hooks/useHotWalletPluginTokenAccounts.ts @@ -0,0 +1,112 @@ +import { MintInfo, u64 } from '@solana/spl-token' +import BigNumber from 'bignumber.js' +import { PublicKey } from '@solana/web3.js' +import { + getOwnedTokenAccounts, + TokenProgramAccount, + tryGetMint, +} from '@utils/tokens' +import { useCallback, useEffect, useState } from 'react' +import useWalletStore from 'stores/useWalletStore' +import tokenService from '@utils/services/token' +import { BN } from '@project-serum/anchor' +import { HotWalletAccount } from './useHotWallet' +import { getSplTokenNameByMint } from '@utils/splTokens' + +export type HotWalletTokenAccounts = { + publicKey: PublicKey + mint: PublicKey + decimals: number + amount: u64 + mintName?: string + usdMintValue: number + usdTotalValue: u64 +}[] + +const useHotWalletPluginTokenAccounts = ( + hotWalletAccount: HotWalletAccount +) => { + const connection = useWalletStore((store) => store.connection) + const [ + tokenAccounts, + setTokenAccounts, + ] = useState(null) + + const loadTokenAccounts = useCallback(async () => { + if (!connection.current) return + + const ownedTokenAccounts = await getOwnedTokenAccounts( + connection.current, + hotWalletAccount.publicKey + ) + + const tokenMintAddresses = [ + ...new Set(ownedTokenAccounts.map(({ account: { mint } }) => mint)), + ] + + const mintInfos = ( + await Promise.all( + tokenMintAddresses.map((tokenMintAddress) => + tryGetMint(connection.current, tokenMintAddress) + ) + ) + ).reduce( + (acc, mintInfo) => { + if (!mintInfo) throw new Error('Cannot load mint info') + + acc[mintInfo.publicKey.toBase58()] = { + ...mintInfo, + name: getSplTokenNameByMint(mintInfo.publicKey), + usdValue: tokenService.getUSDTokenPrice( + mintInfo.publicKey.toBase58() + ), + } + + return acc + }, + {} as { + [key: string]: TokenProgramAccount & { + name?: string + usdValue: number + } + } + ) + + setTokenAccounts( + ownedTokenAccounts + .map((tokenAccount) => { + const mintInfo = mintInfos[tokenAccount.account.mint.toBase58()] + + return { + mint: tokenAccount.account.mint, + publicKey: tokenAccount.publicKey, + amount: tokenAccount.account.amount, + decimals: mintInfo.account.decimals, + mintName: mintInfo.name, + usdMintValue: mintInfo.usdValue, + usdTotalValue: new BN( + new BigNumber(tokenAccount.account.amount.toString()) + .multipliedBy(mintInfo.usdValue) + .integerValue() + .toString() + ), + } + }) + .sort((a, b) => b.amount.toNumber() - a.amount.toNumber()) + ) + }, [ + connection, + JSON.stringify(tokenService._tokenPriceToUSDlist), + hotWalletAccount, + ]) + + useEffect(() => { + loadTokenAccounts() + }, [loadTokenAccounts]) + + return { + tokenAccounts, + } +} + +export default useHotWalletPluginTokenAccounts diff --git a/hooks/useSaberStats.ts b/hooks/useSaberStats.ts new file mode 100644 index 0000000000..68ffbadec9 --- /dev/null +++ b/hooks/useSaberStats.ts @@ -0,0 +1,117 @@ +import { PublicKey } from '@solana/web3.js' +import { useCallback, useEffect, useState } from 'react' +import { + SolanaAugmentedProvider, + SolanaProvider, +} from '@saberhq/solana-contrib' +import { Wallet } from '@project-serum/common' +import useWalletStore from 'stores/useWalletStore' +import { + findMinerAddress, + findQuarryAddress, + QuarrySDK, +} from '@quarryprotocol/quarry-sdk' + +import QuarryMineConfiguration, { + SABER_UXD_USDC_LP, +} from '@tools/sdk/quarryMine/configuration' + +import { tryGetMint } from '@utils/tokens' +import { BN } from '@project-serum/anchor' +import BigNumber from 'bignumber.js' +import { getSplTokenNameByMint } from '@utils/splTokens' +import { HotWalletAccount } from './useHotWallet' + +export type SaberStats = { + liquidityPoolName: string + balance: BN + uiBalance: number + pendingRewards: { + mint: PublicKey + name: string + uiPendingAmount: number + }[] + mintName: string + rewardsTokenMintName: string +} + +const useSaberStats = (hotWalletAccount: HotWalletAccount) => { + const connection = useWalletStore((store) => store.connection) + const wallet = useWalletStore((s) => s.current) + + const [saberStats, setSaberStats] = useState(null) + + const loadInfo = useCallback(async () => { + if (!connection.current || !hotWalletAccount) return + + try { + const { + mint, + rewarder, + mintName, + rewardsTokenMintName, + } = QuarryMineConfiguration.mintSpecificAddresses[SABER_UXD_USDC_LP] + + const [quarry] = await findQuarryAddress(rewarder, mint) + const [miner] = await findMinerAddress(quarry, hotWalletAccount.publicKey) + + const sdk = QuarrySDK.load({ + provider: new SolanaAugmentedProvider( + SolanaProvider.load({ + connection: connection.current, + sendConnection: connection.current, + wallet: wallet as Wallet, + }) + ), + }) + + const sonarData = await fetch( + 'https://api-uxd.sonar.watch/uxd' + ).then((res) => res.json()) + const saberAccountSonarData = sonarData.find( + ({ platform, owner }) => + platform === 'quarry' && + owner === hotWalletAccount.publicKey.toBase58() + ) + + const pendingRewards = saberAccountSonarData.rewardAssets.map( + (asset) => ({ + mint: new PublicKey(asset.mint), + name: getSplTokenNameByMint(new PublicKey(asset.mint)), + uiPendingAmount: asset.pending, + }) + ) + + const minerData = await sdk.programs.Mine.account.miner.fetch(miner) + + const lpMintInfo = await tryGetMint(connection.current, mint) + if (!lpMintInfo) + throw new Error(`Cannot load lp mint info for ${mint.toBase58()}`) + + setSaberStats([ + { + liquidityPoolName: 'Saber UXD-USDC Liquidity Pool', + balance: minerData.balance, + uiBalance: new BigNumber(minerData.balance.toString()) + .shiftedBy(-lpMintInfo.account.decimals) + .toNumber(), + pendingRewards, + mintName, + rewardsTokenMintName, + }, + ]) + } catch (err) { + console.log('error loading saber stats', err) + } + }, [connection, hotWalletAccount, wallet]) + + useEffect(() => { + loadInfo() + }, [loadInfo]) + + return { + saberStats, + } +} + +export default useSaberStats diff --git a/hooks/useTribecaGauge.ts b/hooks/useTribecaGauge.ts new file mode 100644 index 0000000000..7067356189 --- /dev/null +++ b/hooks/useTribecaGauge.ts @@ -0,0 +1,30 @@ +import { useCallback, useEffect, useState } from 'react' +import ATribecaConfiguration, { + GaugeInfos, +} from '@tools/sdk/tribeca/ATribecaConfiguration' +import useTribecaPrograms from './useTribecaPrograms' + +export default function useTribecaGauge( + tribecaConfiguration: ATribecaConfiguration | null +) { + const { programs } = useTribecaPrograms(tribecaConfiguration) + + const [gauges, setGauges] = useState(null) + + const loadGauges = useCallback(async (): Promise => { + if (!tribecaConfiguration || !programs) { + return null + } + + return tribecaConfiguration.fetchAllGauge(programs) + }, [tribecaConfiguration, programs]) + + useEffect(() => { + loadGauges().then(setGauges) + }, [loadGauges]) + + return { + gauges, + programs, + } +} diff --git a/hooks/useTribecaGaugesInfos.ts b/hooks/useTribecaGaugesInfos.ts new file mode 100644 index 0000000000..a02461c23d --- /dev/null +++ b/hooks/useTribecaGaugesInfos.ts @@ -0,0 +1,213 @@ +import { useCallback, useEffect, useState } from 'react' +import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' +import useTribecaGauge from './useTribecaGauge' +import { PublicKey } from '@solana/web3.js' +import useRealm from './useRealm' +import { + EpochGaugeVoterData, + EscrowData, + GaugemeisterData, + GaugeVoterData, +} from '@tools/sdk/tribeca/programs' + +const EscrowOwnerMap = { + UXDProtocol: { + name: `SOL Treasury's Owner`, + publicKey: new PublicKey('7M6TSEkRiXiYmpRCcCDSdJGTGxAPem2HBqjW4gLQ2KoE'), + }, + 'Kek World': { + name: `SOL Treasury's Owner`, + publicKey: new PublicKey('AuQHcJZhTd1dnXRrM78RomFiCvW6a9CqxxJ94Fp9h8b'), + }, +} + +export type ActiveGaugeVoteData = { + name: string + mint: PublicKey + logoURI?: string + weight: number + weightPercentage: number +} + +export type TribecaGaugesInfos = { + escrowData: EscrowData + gaugemeisterData: GaugemeisterData + gaugeVoterData: GaugeVoterData | null + activeGaugeVotesData: ActiveGaugeVoteData[] | null + currentEpochGaugeVoterData: EpochGaugeVoterData | null + nextEpochGaugeVoterData: EpochGaugeVoterData | null +} + +export default function useTribecaGaugeInfos( + tribecaConfiguration: ATribecaConfiguration | null +) { + const { realm } = useRealm() + + const [escrowOwner, setEscrowOwner] = useState<{ + name: string + publicKey: PublicKey + } | null>(null) + + useEffect(() => { + if (!realm) return + + setEscrowOwner(EscrowOwnerMap[realm.account.name] ?? null) + }, [realm]) + + const { programs, gauges } = useTribecaGauge(tribecaConfiguration) + + const [infos, setInfos] = useState(null) + + const loadInfos = useCallback(async (): Promise => { + if (!tribecaConfiguration || !programs || !escrowOwner || !gauges) + return null + + try { + const [escrow] = await tribecaConfiguration.findEscrowAddress( + escrowOwner.publicKey + ) + + const [escrowData, gaugemeisterData] = await Promise.all([ + programs.LockedVoter.account.escrow.fetch(escrow), + + programs.Gauge.account.gaugemeister.fetch( + ATribecaConfiguration.gaugemeister + ), + ]) + + let gaugeVoter: PublicKey + + try { + const [publicKey] = await tribecaConfiguration.findGaugeVoterAddress( + escrow + ) + + gaugeVoter = publicKey + } catch (_) { + // means we have no gaugeVoter + return { + escrowData, + gaugemeisterData, + gaugeVoterData: null, + activeGaugeVotesData: null, + currentEpochGaugeVoterData: null, + nextEpochGaugeVoterData: null, + } + } + + let gaugeVoterData: GaugeVoterData + + try { + gaugeVoterData = await programs.Gauge.account.gaugeVoter.fetch( + gaugeVoter + ) + } catch (_) { + // Gauge voter has not been initialized + return { + escrowData, + gaugemeisterData, + gaugeVoterData: null, + activeGaugeVotesData: null, + currentEpochGaugeVoterData: null, + nextEpochGaugeVoterData: null, + } + } + + const gaugeVotes = await programs.Gauge.account.gaugeVote.all() + + const activeGaugeVotes = gaugeVotes.filter( + (gaugeVote) => + gaugeVote.account.weight > 0 && + gaugeVote.account.gaugeVoter.equals(gaugeVoter) + ) + + const totalRelativeGaugeVotesWeight = activeGaugeVotes.reduce( + (totalWeight, activeGaugeVote) => + totalWeight + activeGaugeVote.account.weight, + 0 + ) + + const activeGaugeVotesData = activeGaugeVotes.map((activeGaugeVote) => { + const [name, gaugeInfos] = Object.entries(gauges).find(([, gauge]) => + gauge.publicKey.equals(activeGaugeVote.account.gauge) + )! + + return { + name, + mint: gaugeInfos.mint, + logoURI: gaugeInfos.logoURI, + weight: activeGaugeVote.account.weight, + weightPercentage: Number( + ( + (activeGaugeVote.account.weight * 100) / + totalRelativeGaugeVotesWeight + ).toFixed(2) + ), + } + }) + + let currentEpochGaugeVoterData: EpochGaugeVoterData | null = null + + try { + const [ + currentEpochGaugeVoter, + ] = await tribecaConfiguration.findEpochGaugeVoterAddress( + gaugeVoter, + gaugemeisterData.currentRewardsEpoch + ) + + currentEpochGaugeVoterData = await programs.Gauge.account.epochGaugeVoter.fetch( + currentEpochGaugeVoter + ) + } catch (_) { + // ignore error, means we have not voted on the epoch + } + + let nextEpochGaugeVoterData: EpochGaugeVoterData | null = null + + try { + const [ + nextEpochGaugeVoter, + ] = await tribecaConfiguration.findEpochGaugeVoterAddress( + gaugeVoter, + gaugemeisterData.currentRewardsEpoch + 1 + ) + + nextEpochGaugeVoterData = await programs.Gauge.account.epochGaugeVoter.fetch( + nextEpochGaugeVoter + ) + } catch (_) { + // ignore error, means we have not voted on the epoch + } + + return { + escrowData, + gaugemeisterData, + gaugeVoterData, + activeGaugeVotesData, + currentEpochGaugeVoterData, + nextEpochGaugeVoterData, + } + } catch (err) { + console.log( + `Cannot load Gauges infos for escrowOwner ${ + escrowOwner.name + } / ${escrowOwner.publicKey.toString()}`, + err + ) + + return null + } + }, [tribecaConfiguration, programs, escrowOwner, gauges]) + + useEffect(() => { + loadInfos().then(setInfos) + }, [loadInfos]) + + return { + escrowOwner, + infos, + gauges, + programs, + } +} diff --git a/hooks/useTribecaLockerData.ts b/hooks/useTribecaLockerData.ts new file mode 100644 index 0000000000..61111fb38b --- /dev/null +++ b/hooks/useTribecaLockerData.ts @@ -0,0 +1,31 @@ +import { useCallback, useEffect, useState } from 'react' +import { LockerData } from '@tools/sdk/tribeca/programs' +import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' +import useTribecaPrograms from './useTribecaPrograms' + +export default function useTribecaLockerData( + tribecaConfiguration: ATribecaConfiguration | null +) { + const { programs } = useTribecaPrograms(tribecaConfiguration) + + const [lockerData, setLockerData] = useState(null) + + const loadLockerData = useCallback(async (): Promise => { + if (!programs || !tribecaConfiguration) { + return null + } + + return programs.LockedVoter.account.locker.fetch( + tribecaConfiguration.locker + ) + }, [programs, tribecaConfiguration]) + + useEffect(() => { + loadLockerData().then(setLockerData) + }, [loadLockerData]) + + return { + lockerData, + programs, + } +} diff --git a/hooks/useTribecaPrograms.ts b/hooks/useTribecaPrograms.ts new file mode 100644 index 0000000000..68f5fe11a8 --- /dev/null +++ b/hooks/useTribecaPrograms.ts @@ -0,0 +1,42 @@ +import { Wallet } from '@project-serum/common' +import { + SolanaAugmentedProvider, + SolanaProvider, +} from '@saberhq/solana-contrib' +import { useEffect, useState } from 'react' + +import useWalletStore from 'stores/useWalletStore' +import ATribecaConfiguration, { + TribecaPrograms, +} from '@tools/sdk/tribeca/ATribecaConfiguration' + +export default function useTribecaPrograms( + tribecaConfiguration: ATribecaConfiguration | null +) { + const connection = useWalletStore((s) => s.connection) + const wallet = useWalletStore((s) => s.current) + + const [programs, setPrograms] = useState(null) + + useEffect(() => { + if (!connection || !wallet || !tribecaConfiguration) { + return + } + + const solanaProvider = SolanaProvider.load({ + connection: connection.current, + sendConnection: connection.current, + wallet: wallet as Wallet, + }) + + setPrograms( + tribecaConfiguration.loadPrograms( + new SolanaAugmentedProvider(solanaProvider) + ) + ) + }, [connection, wallet, tribecaConfiguration]) + + return { + programs, + } +} diff --git a/package.json b/package.json index 11a21e098c..189ead00d4 100644 --- a/package.json +++ b/package.json @@ -34,11 +34,15 @@ "@marinade.finance/marinade-ts-sdk": "^2.0.9", "@next/bundle-analyzer": "^12.1.0", "@nfteyez/sol-rayz": "^0.8.0", - "@project-serum/borsh": "^0.2.2", - "@project-serum/anchor": "^0.20.1", + "@project-serum/anchor": "^0.22.1", + "@project-serum/borsh": "^0.2.5", "@project-serum/common": "^0.0.1-beta.3", "@project-serum/sol-wallet-adapter": "^0.2.0", "@raydium-io/raydium-sdk": "^1.0.1-beta.34", + "@saberhq/anchor-contrib": "^1.12.56", + "@quarryprotocol/quarry-sdk": "^3.0.0", + "@saberhq/stableswap-sdk": "^1.12.55", + "@saberhq/token-utils": "^1.12.55", "@solana/spl-governance": "^0.0.28", "@solana/spl-token": "^0.1.3", "@solana/spl-token-registry": "^0.2.1733", diff --git a/pages/dao/[symbol]/index.tsx b/pages/dao/[symbol]/index.tsx index 08f3ee9699..d1f4d328ed 100644 --- a/pages/dao/[symbol]/index.tsx +++ b/pages/dao/[symbol]/index.tsx @@ -18,6 +18,7 @@ import ApproveAllBtn from './proposal/components/ApproveAllBtn' import dynamic from 'next/dynamic' import Loading from '@components/Loading' import PaginationComponent from '@components/Pagination' +import HotWallet from '@components/HotWallet/HotWallet' const AccountsCompactWrapper = dynamic( () => import('@components/TreasuryAccount/AccountsCompactWrapper') ) @@ -253,11 +254,17 @@ const REALM = () => { {realm && ( <> + + + + {!realm?.account.config.useCommunityVoterWeightAddin && ( )} + + )} diff --git a/tools/sdk/quarryMine/configuration.ts b/tools/sdk/quarryMine/configuration.ts new file mode 100644 index 0000000000..a66b48f234 --- /dev/null +++ b/tools/sdk/quarryMine/configuration.ts @@ -0,0 +1,61 @@ +import { QUARRY_ADDRESSES } from '@quarryprotocol/quarry-sdk' +import { PublicKey } from '@solana/web3.js' + +export const SABER_UXD_USDC_LP = 'UXD-USDC LP' + +export type SupportedMintName = 'UXD-USDC LP' + +class QuarryMine { + public readonly mintWrapperProgram = new PublicKey( + 'QMWoBmAyJLAsA1Lh9ugMTw2gciTihncciphzdNzdZYV' + ) + + public readonly quarryMineProgram = QUARRY_ADDRESSES.Mine + + public readonly mintSpecificAddresses: { + [key in SupportedMintName]: { + mint: PublicKey + mintName: string + rewardsTokenMintName: string + rewarder: PublicKey + mintWrapper: PublicKey + claimFeeTokenAccount: PublicKey + rewardsTokenMint: PublicKey + rewardsTokenMintDecimals: number + minter: PublicKey + } + } = { + [SABER_UXD_USDC_LP]: { + mintName: 'Saber UXD-USDC LP', + rewardsTokenMintName: 'Saber IOU Token', + mint: new PublicKey('UXDgmqLd1roNYkC4TmJzok61qcM9oKs5foDADiFoCiJ'), + rewardsTokenMint: new PublicKey( + 'iouQcQBAiEXe6cKLS85zmZxUqaCqBdeHFpqKoSz615u' + ), + rewardsTokenMintDecimals: 6, + rewarder: new PublicKey('rXhAofQCT7NN9TUqigyEAUzV1uLL4boeD8CRkNBSkYk'), + mintWrapper: new PublicKey( + 'EVVDA3ZiAjTizemLGXNUN3gb6cffQFEYkFjFZokPmUPz' + ), + claimFeeTokenAccount: new PublicKey( + '4Snkea6wv3K6qzDTdyJiF2VTiLPmCoyHJCzAdkdTStBK' + ), + minter: new PublicKey('GEoTC3gN12qHDniaDD7Zxvd5xtcZyEKkTPy42B44s82y'), + }, + } + + public readonly supportedMintNames: { + [key in SupportedMintName]: true + } = { + 'UXD-USDC LP': true, + } + + public readonly quarryMineProgramInstructions = { + createMiner: 126, + stakeTokens: 136, + withdraw: 2, + claimRewards: 4, + } +} + +export default new QuarryMine() diff --git a/tools/sdk/tribeca/ATribecaConfiguration.ts b/tools/sdk/tribeca/ATribecaConfiguration.ts new file mode 100644 index 0000000000..ae90067a7c --- /dev/null +++ b/tools/sdk/tribeca/ATribecaConfiguration.ts @@ -0,0 +1,288 @@ +import { utils } from '@project-serum/anchor' +import { newProgramMap } from '@saberhq/anchor-contrib' +import { SolanaAugmentedProvider } from '@saberhq/solana-contrib' +import { PublicKey } from '@solana/web3.js' +import { + GaugemeisterData, + GaugeProgram, + MineProgram, + QuarryMineJSON, + UgaugeJSON, +} from './programs' +import { GovernProgram, UgovernJSON } from './programs/govern' +import { LockedVoterProgram, UlockedUvoterJSON } from './programs/lockedVoter' + +export type TribecaPrograms = { + LockedVoter: LockedVoterProgram + Govern: GovernProgram + Gauge: GaugeProgram + Mine: MineProgram +} + +export type GaugeInfo = { + publicKey: PublicKey + mint: PublicKey + logoURI?: string +} + +export type GaugeInfos = { + [name: string]: GaugeInfo +} + +export default abstract class ATribecaConfiguration { + protected encodeU32(num: number): Buffer { + const buf = Buffer.alloc(4) + buf.writeUInt32LE(num) + return buf + } + + public static readonly mintInfoEndpoint = + 'https://cdn.jsdelivr.net/gh/CLBExchange/certified-token-list/101' + + public static readonly lockedVoterProgramId = new PublicKey( + 'LocktDzaV1W2Bm9DeZeiyz4J9zs4fRqNiYqQyracRXw' + ) + + public static readonly governProgramId = new PublicKey( + 'Govz1VyoyLD5BL6CSCxUJLVLsQHRwjfFj1prNsdNg5Jw' + ) + + public static readonly gaugeProgramId = new PublicKey( + 'GaugesLJrnVjNNWLReiw3Q7xQhycSBRgeHGTMDUaX231' + ) + + public static readonly quarryMineProgramId = new PublicKey( + 'QMNeHCGYnLVDn1icRAfQZpjPLBNkfGbSKRB83G5d8KB' + ) + + public static readonly gaugemeister = new PublicKey( + '28ZDtf6d2wsYhBvabTxUHTRT6MDxqjmqR7RMCp348tyU' + ) + + // ---------------- Abstract ---------------- + public abstract readonly name: string + + public abstract readonly locker: PublicKey + + public abstract readonly token: { + name: string + mint: PublicKey + decimals: number + } + // ------------------------------------------ + + public static readonly gaugeInstructions = { + createGaugeVoter: 135, + createGaugeVote: 109, + setGaugeVote: 245, + prepareEpochGaugeVoter: 15, + gaugeCommitVote: 249, + resetEpochGaugeVoter: 21, + gaugeRevertVote: 185, + } + + public static readonly lockedVoterInstructions = { + newEscrow: 216, + lock: 21, + } + + public async findEscrowAddress( + authority: PublicKey + ): Promise<[PublicKey, number]> { + return PublicKey.findProgramAddress( + [ + utils.bytes.utf8.encode('Escrow'), + this.locker.toBuffer(), + authority.toBuffer(), + ], + + ATribecaConfiguration.lockedVoterProgramId + ) + } + + public async findGaugeVoterAddress( + escrow: PublicKey + ): Promise<[PublicKey, number]> { + return PublicKey.findProgramAddress( + [ + utils.bytes.utf8.encode('GaugeVoter'), + ATribecaConfiguration.gaugemeister.toBuffer(), + escrow.toBuffer(), + ], + ATribecaConfiguration.gaugeProgramId + ) + } + + public async findGaugeVoteAddress( + gaugeVoter: PublicKey, + gauge: PublicKey + ): Promise<[PublicKey, number]> { + return PublicKey.findProgramAddress( + [ + utils.bytes.utf8.encode('GaugeVote'), + gaugeVoter.toBuffer(), + gauge.toBuffer(), + ], + ATribecaConfiguration.gaugeProgramId + ) + } + + public async findEpochGaugeVoterAddress( + gaugeVoter: PublicKey, + votingEpoch: number + ): Promise<[PublicKey, number]> { + return PublicKey.findProgramAddress( + [ + utils.bytes.utf8.encode('EpochGaugeVoter'), + gaugeVoter.toBuffer(), + this.encodeU32(votingEpoch), + ], + ATribecaConfiguration.gaugeProgramId + ) + } + + public async findEpochGaugeAddress( + gauge: PublicKey, + votingEpoch: number + ): Promise<[PublicKey, number]> { + return PublicKey.findProgramAddress( + [ + utils.bytes.utf8.encode('EpochGauge'), + gauge.toBuffer(), + this.encodeU32(votingEpoch), + ], + ATribecaConfiguration.gaugeProgramId + ) + } + + public async findEpochGaugeVoteAddress( + gaugeVote: PublicKey, + votingEpoch: number + ): Promise<[PublicKey, number]> { + return PublicKey.findProgramAddress( + [ + utils.bytes.utf8.encode('EpochGaugeVote'), + gaugeVote.toBuffer(), + this.encodeU32(votingEpoch), + ], + ATribecaConfiguration.gaugeProgramId + ) + } + + public async findWhitelistAddress( + locker: PublicKey, + programId: PublicKey, + owner: PublicKey + ): Promise<[PublicKey, number]> { + return PublicKey.findProgramAddress( + [ + utils.bytes.utf8.encode('LockerWhitelistEntry'), + locker.toBuffer(), + programId.toBuffer(), + owner.toBuffer(), + ], + ATribecaConfiguration.lockedVoterProgramId + ) + } + + public async fetchAllGauge(programs: TribecaPrograms): Promise { + const gauges = (await programs.Gauge.account.gauge.all()).filter( + ({ account: { isDisabled, gaugemeister } }) => + !isDisabled || + gaugemeister.toString() !== + ATribecaConfiguration.gaugemeister.toString() + ) + + // Fetch the info of the quarry behind the gauge to get name + logo behind the gauge + const quarryInfos = await programs.Mine.account.quarry.fetchMultiple( + gauges.map((x) => x.account.quarry) + ) + + if (!quarryInfos) { + throw new Error('Cannot load quarry infos') + } + + const mints = quarryInfos.map((x) => (x as any).tokenMintKey) + + const infosInArray = await Promise.all( + mints.map((x, xi) => + (async () => { + try { + const response = await fetch( + `${ATribecaConfiguration.mintInfoEndpoint}/${x.toString()}.json`, + { + method: 'GET', + headers: { + Accept: 'application/json', + }, + } + ) + + const { name, logoURI } = await response.json() + + return { + publicKey: gauges[xi].publicKey, + mint: gauges[xi].publicKey, + logoURI, + name, + } + } catch { + return { + publicKey: gauges[xi].publicKey, + name: x, + mint: x, + } + } + })() + ) + ) + + return infosInArray.reduce((gaugeInfos, { name, ...other }) => { + return { + ...gaugeInfos, + + [name]: { + ...other, + }, + } + }, {}) + } + + public async fetchGaugemeister( + gaugeProgram: GaugeProgram + ): Promise { + const data = await gaugeProgram.account.gaugemeister.fetchNullable( + ATribecaConfiguration.gaugemeister + ) + + if (!data) { + throw new Error( + `Empty Gaugemeister data ${ATribecaConfiguration.gaugemeister}` + ) + } + + return data + } + + public loadPrograms(provider: SolanaAugmentedProvider): TribecaPrograms { + return newProgramMap( + provider, + + { + // IDLs + LockedVoter: UlockedUvoterJSON, + Govern: UgovernJSON, + Gauge: UgaugeJSON, + Mine: QuarryMineJSON, + }, + + { + // Addresses + LockedVoter: ATribecaConfiguration.lockedVoterProgramId, + Govern: ATribecaConfiguration.governProgramId, + Gauge: ATribecaConfiguration.gaugeProgramId, + Mine: ATribecaConfiguration.quarryMineProgramId, + } + ) + } +} diff --git a/tools/sdk/tribeca/configurations.ts b/tools/sdk/tribeca/configurations.ts new file mode 100644 index 0000000000..b6a06717cd --- /dev/null +++ b/tools/sdk/tribeca/configurations.ts @@ -0,0 +1,39 @@ +import { PublicKey } from '@solana/web3.js' +import { SPL_TOKENS } from '@utils/splTokens' +import ATribecaConfiguration from './ATribecaConfiguration' + +class SaberTribecaConfiguration extends ATribecaConfiguration { + public readonly locker = new PublicKey( + '8erad8kmNrLJDJPe9UkmTHomrMV3EW48sjGeECyVjbYX' + ) + + public readonly token = SPL_TOKENS.SBR + public readonly name = 'Saber' +} + +class SunnyTribecaConfiguration extends ATribecaConfiguration { + public readonly locker = new PublicKey( + '4tr9CDSgZRLYPGdcsm9PztaGSfJtX5CEmqDbEbvCTX2G' + ) + + public readonly token = SPL_TOKENS.SUNNY + public readonly name = 'Sunny' +} + +export const saberTribecaConfiguration = new SaberTribecaConfiguration() +export const sunnyTribecaConfiguration = new SunnyTribecaConfiguration() + +export const configurations = { + saber: saberTribecaConfiguration, + sunny: sunnyTribecaConfiguration, +} + +export function getConfigurationByName( + name: string +): ATribecaConfiguration | null { + return ( + Object.values(configurations).find( + (configuration) => configuration.name === name + ) ?? null + ) +} diff --git a/tools/sdk/tribeca/idls/gauge.ts b/tools/sdk/tribeca/idls/gauge.ts new file mode 100644 index 0000000000..a4756f8208 --- /dev/null +++ b/tools/sdk/tribeca/idls/gauge.ts @@ -0,0 +1,2471 @@ +import { generateErrorMap } from '@saberhq/anchor-contrib' + +export type UgaugeIDL = { + version: '0.0.0' + name: 'gauge' + instructions: [ + { + name: 'createGaugemeister' + accounts: [ + { + name: 'gaugemeister' + isMut: true + isSigner: false + }, + { + name: 'base' + isMut: false + isSigner: true + }, + { + name: 'operator' + isMut: false + isSigner: false + }, + { + name: 'locker' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + }, + { + name: 'foreman' + type: 'publicKey' + }, + { + name: 'epochDurationSeconds' + type: 'u32' + }, + { + name: 'firstEpochStartsAt' + type: 'u64' + } + ] + }, + { + name: 'createGauge' + accounts: [ + { + name: 'gauge' + isMut: true + isSigner: false + }, + { + name: 'gaugemeister' + isMut: false + isSigner: false + }, + { + name: 'quarry' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + } + ] + }, + { + name: 'createGaugeVoter' + accounts: [ + { + name: 'gaugeVoter' + isMut: true + isSigner: false + }, + { + name: 'gaugemeister' + isMut: false + isSigner: false + }, + { + name: 'escrow' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + } + ] + }, + { + name: 'createGaugeVote' + accounts: [ + { + name: 'gaugeVote' + isMut: true + isSigner: false + }, + { + name: 'gaugeVoter' + isMut: false + isSigner: false + }, + { + name: 'gauge' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + } + ] + }, + { + name: 'createEpochGauge' + accounts: [ + { + name: 'gauge' + isMut: false + isSigner: false + }, + { + name: 'epochGauge' + isMut: true + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + }, + { + name: 'votingEpoch' + type: 'u32' + } + ] + }, + { + name: 'prepareEpochGaugeVoter' + accounts: [ + { + name: 'gaugemeister' + isMut: false + isSigner: false + }, + { + name: 'locker' + isMut: false + isSigner: false + }, + { + name: 'escrow' + isMut: false + isSigner: false + }, + { + name: 'gaugeVoter' + isMut: false + isSigner: false + }, + { + name: 'epochGaugeVoter' + isMut: true + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + } + ] + }, + { + name: 'resetEpochGaugeVoter' + accounts: [ + { + name: 'gaugemeister' + isMut: false + isSigner: false + }, + { + name: 'locker' + isMut: false + isSigner: false + }, + { + name: 'escrow' + isMut: false + isSigner: false + }, + { + name: 'gaugeVoter' + isMut: false + isSigner: false + }, + { + name: 'epochGaugeVoter' + isMut: true + isSigner: false + } + ] + args: [] + }, + { + name: 'gaugeSetVote' + accounts: [ + { + name: 'gaugemeister' + isMut: false + isSigner: false + }, + { + name: 'gauge' + isMut: false + isSigner: false + }, + { + name: 'gaugeVoter' + isMut: true + isSigner: false + }, + { + name: 'gaugeVote' + isMut: true + isSigner: false + }, + { + name: 'escrow' + isMut: false + isSigner: false + }, + { + name: 'voteDelegate' + isMut: false + isSigner: true + } + ] + args: [ + { + name: 'weight' + type: 'u32' + } + ] + }, + { + name: 'gaugeCommitVote' + accounts: [ + { + name: 'gaugemeister' + isMut: false + isSigner: false + }, + { + name: 'gauge' + isMut: false + isSigner: false + }, + { + name: 'gaugeVoter' + isMut: false + isSigner: false + }, + { + name: 'gaugeVote' + isMut: false + isSigner: false + }, + { + name: 'epochGauge' + isMut: true + isSigner: false + }, + { + name: 'epochGaugeVoter' + isMut: true + isSigner: false + }, + { + name: 'epochGaugeVote' + isMut: true + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'voteBump' + type: 'u8' + } + ] + }, + { + name: 'gaugeRevertVote' + accounts: [ + { + name: 'gaugemeister' + isMut: false + isSigner: false + }, + { + name: 'gauge' + isMut: false + isSigner: false + }, + { + name: 'gaugeVoter' + isMut: false + isSigner: false + }, + { + name: 'gaugeVote' + isMut: false + isSigner: false + }, + { + name: 'epochGauge' + isMut: true + isSigner: false + }, + { + name: 'epochGaugeVoter' + isMut: true + isSigner: false + }, + { + name: 'escrow' + isMut: false + isSigner: false + }, + { + name: 'voteDelegate' + isMut: false + isSigner: true + }, + { + name: 'epochGaugeVote' + isMut: true + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + } + ] + args: [] + }, + { + name: 'gaugeEnable' + accounts: [ + { + name: 'gaugemeister' + isMut: false + isSigner: false + }, + { + name: 'gauge' + isMut: true + isSigner: false + }, + { + name: 'foreman' + isMut: false + isSigner: true + } + ] + args: [] + }, + { + name: 'gaugeDisable' + accounts: [ + { + name: 'gaugemeister' + isMut: false + isSigner: false + }, + { + name: 'gauge' + isMut: true + isSigner: false + }, + { + name: 'foreman' + isMut: false + isSigner: true + } + ] + args: [] + }, + { + name: 'triggerNextEpoch' + accounts: [ + { + name: 'gaugemeister' + isMut: true + isSigner: false + } + ] + args: [] + }, + { + name: 'syncGauge' + accounts: [ + { + name: 'gaugemeister' + isMut: false + isSigner: false + }, + { + name: 'gauge' + isMut: false + isSigner: false + }, + { + name: 'epochGauge' + isMut: false + isSigner: false + }, + { + name: 'quarry' + isMut: true + isSigner: false + }, + { + name: 'operator' + isMut: true + isSigner: false + }, + { + name: 'rewarder' + isMut: true + isSigner: false + }, + { + name: 'quarryMineProgram' + isMut: false + isSigner: false + }, + { + name: 'quarryOperatorProgram' + isMut: false + isSigner: false + } + ] + args: [] + }, + { + name: 'setGaugemeisterParams' + accounts: [ + { + name: 'gaugemeister' + isMut: true + isSigner: false + }, + { + name: 'foreman' + isMut: false + isSigner: true + } + ] + args: [ + { + name: 'newEpochDurationSeconds' + type: 'u32' + }, + { + name: 'newForeman' + type: 'publicKey' + } + ] + } + ] + accounts: [ + { + name: 'Gaugemeister' + type: { + kind: 'struct' + fields: [ + { + name: 'base' + type: 'publicKey' + }, + { + name: 'bump' + type: 'u8' + }, + { + name: 'rewarder' + type: 'publicKey' + }, + { + name: 'operator' + type: 'publicKey' + }, + { + name: 'locker' + type: 'publicKey' + }, + { + name: 'foreman' + type: 'publicKey' + }, + { + name: 'epochDurationSeconds' + type: 'u32' + }, + { + name: 'currentRewardsEpoch' + type: 'u32' + }, + { + name: 'nextEpochStartsAt' + type: 'u64' + }, + { + name: 'lockerTokenMint' + type: 'publicKey' + }, + { + name: 'lockerGovernor' + type: 'publicKey' + } + ] + } + }, + { + name: 'Gauge' + type: { + kind: 'struct' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + }, + { + name: 'quarry' + type: 'publicKey' + }, + { + name: 'isDisabled' + type: 'bool' + } + ] + } + }, + { + name: 'GaugeVoter' + type: { + kind: 'struct' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + }, + { + name: 'escrow' + type: 'publicKey' + }, + { + name: 'owner' + type: 'publicKey' + }, + { + name: 'totalWeight' + type: 'u32' + }, + { + name: 'weightChangeSeqno' + type: 'u64' + } + ] + } + }, + { + name: 'GaugeVote' + type: { + kind: 'struct' + fields: [ + { + name: 'gaugeVoter' + type: 'publicKey' + }, + { + name: 'gauge' + type: 'publicKey' + }, + { + name: 'weight' + type: 'u32' + } + ] + } + }, + { + name: 'EpochGauge' + type: { + kind: 'struct' + fields: [ + { + name: 'gauge' + type: 'publicKey' + }, + { + name: 'votingEpoch' + type: 'u32' + }, + { + name: 'totalPower' + type: 'u64' + } + ] + } + }, + { + name: 'EpochGaugeVoter' + type: { + kind: 'struct' + fields: [ + { + name: 'gaugeVoter' + type: 'publicKey' + }, + { + name: 'votingEpoch' + type: 'u32' + }, + { + name: 'weightChangeSeqno' + type: 'u64' + }, + { + name: 'votingPower' + type: 'u64' + }, + { + name: 'allocatedPower' + type: 'u64' + } + ] + } + }, + { + name: 'EpochGaugeVote' + type: { + kind: 'struct' + fields: [ + { + name: 'allocatedPower' + type: 'u64' + } + ] + } + } + ] + events: [ + { + name: 'EpochGaugeCreateEvent' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + index: true + }, + { + name: 'quarry' + type: 'publicKey' + index: true + }, + { + name: 'votingEpoch' + type: 'u32' + index: false + } + ] + }, + { + name: 'GaugeCreateEvent' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + index: true + }, + { + name: 'rewarder' + type: 'publicKey' + index: true + }, + { + name: 'quarry' + type: 'publicKey' + index: true + }, + { + name: 'gaugeVoterOwner' + type: 'publicKey' + index: true + } + ] + }, + { + name: 'GaugeVoteCreateEvent' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + index: true + }, + { + name: 'gauge' + type: 'publicKey' + index: true + }, + { + name: 'quarry' + type: 'publicKey' + index: true + }, + { + name: 'gaugeVoterOwner' + type: 'publicKey' + index: true + } + ] + }, + { + name: 'GaugeVoterCreateEvent' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + index: true + }, + { + name: 'rewarder' + type: 'publicKey' + index: true + }, + { + name: 'gaugeVoterOwner' + type: 'publicKey' + index: true + } + ] + }, + { + name: 'GaugemeisterCreateEvent' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + index: false + }, + { + name: 'rewarder' + type: 'publicKey' + index: false + }, + { + name: 'lockerTokenMint' + type: 'publicKey' + index: false + }, + { + name: 'lockerGovernor' + type: 'publicKey' + index: false + }, + { + name: 'foreman' + type: 'publicKey' + index: false + }, + { + name: 'firstRewardsEpoch' + type: 'u64' + index: false + } + ] + }, + { + name: 'CommitGaugeVoteEvent' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + index: true + }, + { + name: 'gauge' + type: 'publicKey' + index: true + }, + { + name: 'quarry' + type: 'publicKey' + index: true + }, + { + name: 'gaugeVoterOwner' + type: 'publicKey' + index: true + }, + { + name: 'votingEpoch' + type: 'u32' + index: false + }, + { + name: 'voteSharesForNextEpoch' + type: 'u64' + index: false + }, + { + name: 'updatedAllocatedPower' + type: 'u64' + index: false + }, + { + name: 'updatedTotalPower' + type: 'u64' + index: false + } + ] + }, + { + name: 'GaugeDisableEvent' + fields: [ + { + name: 'gauge' + type: 'publicKey' + index: false + }, + { + name: 'gaugemeister' + type: 'publicKey' + index: false + }, + { + name: 'foreman' + type: 'publicKey' + index: false + } + ] + }, + { + name: 'GaugeEnableEvent' + fields: [ + { + name: 'gauge' + type: 'publicKey' + index: false + }, + { + name: 'gaugemeister' + type: 'publicKey' + index: false + }, + { + name: 'foreman' + type: 'publicKey' + index: false + } + ] + }, + { + name: 'RevertGaugeVoteEvent' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + index: true + }, + { + name: 'gauge' + type: 'publicKey' + index: true + }, + { + name: 'quarry' + type: 'publicKey' + index: true + }, + { + name: 'gaugeVoterOwner' + type: 'publicKey' + index: true + }, + { + name: 'votingEpoch' + type: 'u32' + index: false + }, + { + name: 'subtractedPower' + type: 'u64' + index: false + }, + { + name: 'updatedAllocatedPower' + type: 'u64' + index: false + }, + { + name: 'updatedTotalPower' + type: 'u64' + index: false + } + ] + }, + { + name: 'SetGaugeVoteEvent' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + index: true + }, + { + name: 'gauge' + type: 'publicKey' + index: true + }, + { + name: 'quarry' + type: 'publicKey' + index: true + }, + { + name: 'gaugeVoterOwner' + type: 'publicKey' + index: true + }, + { + name: 'voteDelegate' + type: 'publicKey' + index: true + }, + { + name: 'prevTotalWeight' + type: 'u32' + index: false + }, + { + name: 'totalWeight' + type: 'u32' + index: false + }, + { + name: 'weightChangeSeqno' + type: 'u64' + index: false + } + ] + }, + { + name: 'PrepareEpochGaugeVoterEvent' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + index: true + }, + { + name: 'rewarder' + type: 'publicKey' + index: true + }, + { + name: 'locker' + type: 'publicKey' + index: true + }, + { + name: 'gaugeVoterOwner' + type: 'publicKey' + index: true + }, + { + name: 'votingEpoch' + type: 'u32' + index: false + }, + { + name: 'votingPower' + type: 'u64' + index: false + }, + { + name: 'weightChangeSeqno' + type: 'u64' + index: false + } + ] + }, + { + name: 'ResetEpochGaugeVoterEvent' + fields: [ + { + name: 'gaugemeister' + type: 'publicKey' + index: true + }, + { + name: 'gaugeVoterOwner' + type: 'publicKey' + index: true + }, + { + name: 'prevVotingPower' + type: 'u64' + index: false + }, + { + name: 'votingPower' + type: 'u64' + index: false + }, + { + name: 'prevWeightChangeSeqno' + type: 'u64' + index: false + }, + { + name: 'weightChangeSeqno' + type: 'u64' + index: false + } + ] + }, + { + name: 'SetGaugemeisterParamsEvent' + fields: [ + { + name: 'prevForeman' + type: 'publicKey' + index: false + }, + { + name: 'newForeman' + type: 'publicKey' + index: false + }, + { + name: 'prevEpochDurationSeconds' + type: 'u32' + index: false + }, + { + name: 'newEpochDurationSeconds' + type: 'u32' + index: false + } + ] + }, + { + name: 'SyncGaugeEvent' + fields: [ + { + name: 'gauge' + type: 'publicKey' + index: false + }, + { + name: 'gaugemeister' + type: 'publicKey' + index: false + }, + { + name: 'epoch' + type: 'u32' + index: false + }, + { + name: 'previousShare' + type: 'u64' + index: false + }, + { + name: 'newShare' + type: 'u64' + index: false + } + ] + } + ] + errors: [ + { + code: 300 + name: 'UnauthorizedNotForeman' + msg: 'You must be the foreman to perform this action.' + }, + { + code: 301 + name: 'GaugeEpochCannotBeZero' + msg: 'Cannot sync gauges at the 0th epoch.' + }, + { + code: 302 + name: 'GaugeWrongEpoch' + msg: 'The gauge is not set to the current epoch.' + }, + { + code: 303 + name: 'NextEpochNotReached' + msg: 'The start time for the next epoch has not yet been reached.' + }, + { + code: 304 + name: 'CannotVoteMustReset' + msg: 'Must set all votes to 0 before changing votes.' + }, + { + code: 305 + name: 'CannotVoteGaugeDisabled' + msg: 'Cannot vote since gauge is disabled; all you may do is set weight to 0.' + }, + { + code: 306 + name: 'VoteAlreadyCommitted' + msg: 'You have already committed your vote to this gauge.' + }, + { + code: 307 + name: 'CannotCommitGaugeDisabled' + msg: 'Cannot commit votes since gauge is disabled; all you may do is set weight to 0.' + }, + { + code: 308 + name: 'EpochGaugeNotVoting' + msg: 'Voting on this epoch gauge is closed.' + }, + { + code: 309 + name: 'WeightSeqnoChanged' + msg: 'Gauge voter voting weights have been modified since you started committing your votes. Please withdraw your votes and try again.' + }, + { + code: 310 + name: 'EpochClosed' + msg: 'You may no longer modify votes for this epoch.' + }, + { + code: 311 + name: 'AllocatedPowerMustBeZero' + msg: 'You must have zero allocated power in order to reset the epoch gauge.' + } + ] +} +export const UgaugeJSON: UgaugeIDL = { + version: '0.0.0', + name: 'gauge', + instructions: [ + { + name: 'createGaugemeister', + accounts: [ + { + name: 'gaugemeister', + isMut: true, + isSigner: false, + }, + { + name: 'base', + isMut: false, + isSigner: true, + }, + { + name: 'operator', + isMut: false, + isSigner: false, + }, + { + name: 'locker', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'foreman', + type: 'publicKey', + }, + { + name: 'epochDurationSeconds', + type: 'u32', + }, + { + name: 'firstEpochStartsAt', + type: 'u64', + }, + ], + }, + { + name: 'createGauge', + accounts: [ + { + name: 'gauge', + isMut: true, + isSigner: false, + }, + { + name: 'gaugemeister', + isMut: false, + isSigner: false, + }, + { + name: 'quarry', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + ], + }, + { + name: 'createGaugeVoter', + accounts: [ + { + name: 'gaugeVoter', + isMut: true, + isSigner: false, + }, + { + name: 'gaugemeister', + isMut: false, + isSigner: false, + }, + { + name: 'escrow', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + ], + }, + { + name: 'createGaugeVote', + accounts: [ + { + name: 'gaugeVote', + isMut: true, + isSigner: false, + }, + { + name: 'gaugeVoter', + isMut: false, + isSigner: false, + }, + { + name: 'gauge', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + ], + }, + { + name: 'createEpochGauge', + accounts: [ + { + name: 'gauge', + isMut: false, + isSigner: false, + }, + { + name: 'epochGauge', + isMut: true, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'votingEpoch', + type: 'u32', + }, + ], + }, + { + name: 'prepareEpochGaugeVoter', + accounts: [ + { + name: 'gaugemeister', + isMut: false, + isSigner: false, + }, + { + name: 'locker', + isMut: false, + isSigner: false, + }, + { + name: 'escrow', + isMut: false, + isSigner: false, + }, + { + name: 'gaugeVoter', + isMut: false, + isSigner: false, + }, + { + name: 'epochGaugeVoter', + isMut: true, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + ], + }, + { + name: 'resetEpochGaugeVoter', + accounts: [ + { + name: 'gaugemeister', + isMut: false, + isSigner: false, + }, + { + name: 'locker', + isMut: false, + isSigner: false, + }, + { + name: 'escrow', + isMut: false, + isSigner: false, + }, + { + name: 'gaugeVoter', + isMut: false, + isSigner: false, + }, + { + name: 'epochGaugeVoter', + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'gaugeSetVote', + accounts: [ + { + name: 'gaugemeister', + isMut: false, + isSigner: false, + }, + { + name: 'gauge', + isMut: false, + isSigner: false, + }, + { + name: 'gaugeVoter', + isMut: true, + isSigner: false, + }, + { + name: 'gaugeVote', + isMut: true, + isSigner: false, + }, + { + name: 'escrow', + isMut: false, + isSigner: false, + }, + { + name: 'voteDelegate', + isMut: false, + isSigner: true, + }, + ], + args: [ + { + name: 'weight', + type: 'u32', + }, + ], + }, + { + name: 'gaugeCommitVote', + accounts: [ + { + name: 'gaugemeister', + isMut: false, + isSigner: false, + }, + { + name: 'gauge', + isMut: false, + isSigner: false, + }, + { + name: 'gaugeVoter', + isMut: false, + isSigner: false, + }, + { + name: 'gaugeVote', + isMut: false, + isSigner: false, + }, + { + name: 'epochGauge', + isMut: true, + isSigner: false, + }, + { + name: 'epochGaugeVoter', + isMut: true, + isSigner: false, + }, + { + name: 'epochGaugeVote', + isMut: true, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'voteBump', + type: 'u8', + }, + ], + }, + { + name: 'gaugeRevertVote', + accounts: [ + { + name: 'gaugemeister', + isMut: false, + isSigner: false, + }, + { + name: 'gauge', + isMut: false, + isSigner: false, + }, + { + name: 'gaugeVoter', + isMut: false, + isSigner: false, + }, + { + name: 'gaugeVote', + isMut: false, + isSigner: false, + }, + { + name: 'epochGauge', + isMut: true, + isSigner: false, + }, + { + name: 'epochGaugeVoter', + isMut: true, + isSigner: false, + }, + { + name: 'escrow', + isMut: false, + isSigner: false, + }, + { + name: 'voteDelegate', + isMut: false, + isSigner: true, + }, + { + name: 'epochGaugeVote', + isMut: true, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + ], + args: [], + }, + { + name: 'gaugeEnable', + accounts: [ + { + name: 'gaugemeister', + isMut: false, + isSigner: false, + }, + { + name: 'gauge', + isMut: true, + isSigner: false, + }, + { + name: 'foreman', + isMut: false, + isSigner: true, + }, + ], + args: [], + }, + { + name: 'gaugeDisable', + accounts: [ + { + name: 'gaugemeister', + isMut: false, + isSigner: false, + }, + { + name: 'gauge', + isMut: true, + isSigner: false, + }, + { + name: 'foreman', + isMut: false, + isSigner: true, + }, + ], + args: [], + }, + { + name: 'triggerNextEpoch', + accounts: [ + { + name: 'gaugemeister', + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'syncGauge', + accounts: [ + { + name: 'gaugemeister', + isMut: false, + isSigner: false, + }, + { + name: 'gauge', + isMut: false, + isSigner: false, + }, + { + name: 'epochGauge', + isMut: false, + isSigner: false, + }, + { + name: 'quarry', + isMut: true, + isSigner: false, + }, + { + name: 'operator', + isMut: true, + isSigner: false, + }, + { + name: 'rewarder', + isMut: true, + isSigner: false, + }, + { + name: 'quarryMineProgram', + isMut: false, + isSigner: false, + }, + { + name: 'quarryOperatorProgram', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'setGaugemeisterParams', + accounts: [ + { + name: 'gaugemeister', + isMut: true, + isSigner: false, + }, + { + name: 'foreman', + isMut: false, + isSigner: true, + }, + ], + args: [ + { + name: 'newEpochDurationSeconds', + type: 'u32', + }, + { + name: 'newForeman', + type: 'publicKey', + }, + ], + }, + ], + accounts: [ + { + name: 'Gaugemeister', + type: { + kind: 'struct', + fields: [ + { + name: 'base', + type: 'publicKey', + }, + { + name: 'bump', + type: 'u8', + }, + { + name: 'rewarder', + type: 'publicKey', + }, + { + name: 'operator', + type: 'publicKey', + }, + { + name: 'locker', + type: 'publicKey', + }, + { + name: 'foreman', + type: 'publicKey', + }, + { + name: 'epochDurationSeconds', + type: 'u32', + }, + { + name: 'currentRewardsEpoch', + type: 'u32', + }, + { + name: 'nextEpochStartsAt', + type: 'u64', + }, + { + name: 'lockerTokenMint', + type: 'publicKey', + }, + { + name: 'lockerGovernor', + type: 'publicKey', + }, + ], + }, + }, + { + name: 'Gauge', + type: { + kind: 'struct', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + }, + { + name: 'quarry', + type: 'publicKey', + }, + { + name: 'isDisabled', + type: 'bool', + }, + ], + }, + }, + { + name: 'GaugeVoter', + type: { + kind: 'struct', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + }, + { + name: 'escrow', + type: 'publicKey', + }, + { + name: 'owner', + type: 'publicKey', + }, + { + name: 'totalWeight', + type: 'u32', + }, + { + name: 'weightChangeSeqno', + type: 'u64', + }, + ], + }, + }, + { + name: 'GaugeVote', + type: { + kind: 'struct', + fields: [ + { + name: 'gaugeVoter', + type: 'publicKey', + }, + { + name: 'gauge', + type: 'publicKey', + }, + { + name: 'weight', + type: 'u32', + }, + ], + }, + }, + { + name: 'EpochGauge', + type: { + kind: 'struct', + fields: [ + { + name: 'gauge', + type: 'publicKey', + }, + { + name: 'votingEpoch', + type: 'u32', + }, + { + name: 'totalPower', + type: 'u64', + }, + ], + }, + }, + { + name: 'EpochGaugeVoter', + type: { + kind: 'struct', + fields: [ + { + name: 'gaugeVoter', + type: 'publicKey', + }, + { + name: 'votingEpoch', + type: 'u32', + }, + { + name: 'weightChangeSeqno', + type: 'u64', + }, + { + name: 'votingPower', + type: 'u64', + }, + { + name: 'allocatedPower', + type: 'u64', + }, + ], + }, + }, + { + name: 'EpochGaugeVote', + type: { + kind: 'struct', + fields: [ + { + name: 'allocatedPower', + type: 'u64', + }, + ], + }, + }, + ], + events: [ + { + name: 'EpochGaugeCreateEvent', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + index: true, + }, + { + name: 'quarry', + type: 'publicKey', + index: true, + }, + { + name: 'votingEpoch', + type: 'u32', + index: false, + }, + ], + }, + { + name: 'GaugeCreateEvent', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + index: true, + }, + { + name: 'rewarder', + type: 'publicKey', + index: true, + }, + { + name: 'quarry', + type: 'publicKey', + index: true, + }, + { + name: 'gaugeVoterOwner', + type: 'publicKey', + index: true, + }, + ], + }, + { + name: 'GaugeVoteCreateEvent', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + index: true, + }, + { + name: 'gauge', + type: 'publicKey', + index: true, + }, + { + name: 'quarry', + type: 'publicKey', + index: true, + }, + { + name: 'gaugeVoterOwner', + type: 'publicKey', + index: true, + }, + ], + }, + { + name: 'GaugeVoterCreateEvent', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + index: true, + }, + { + name: 'rewarder', + type: 'publicKey', + index: true, + }, + { + name: 'gaugeVoterOwner', + type: 'publicKey', + index: true, + }, + ], + }, + { + name: 'GaugemeisterCreateEvent', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + index: false, + }, + { + name: 'rewarder', + type: 'publicKey', + index: false, + }, + { + name: 'lockerTokenMint', + type: 'publicKey', + index: false, + }, + { + name: 'lockerGovernor', + type: 'publicKey', + index: false, + }, + { + name: 'foreman', + type: 'publicKey', + index: false, + }, + { + name: 'firstRewardsEpoch', + type: 'u64', + index: false, + }, + ], + }, + { + name: 'CommitGaugeVoteEvent', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + index: true, + }, + { + name: 'gauge', + type: 'publicKey', + index: true, + }, + { + name: 'quarry', + type: 'publicKey', + index: true, + }, + { + name: 'gaugeVoterOwner', + type: 'publicKey', + index: true, + }, + { + name: 'votingEpoch', + type: 'u32', + index: false, + }, + { + name: 'voteSharesForNextEpoch', + type: 'u64', + index: false, + }, + { + name: 'updatedAllocatedPower', + type: 'u64', + index: false, + }, + { + name: 'updatedTotalPower', + type: 'u64', + index: false, + }, + ], + }, + { + name: 'GaugeDisableEvent', + fields: [ + { + name: 'gauge', + type: 'publicKey', + index: false, + }, + { + name: 'gaugemeister', + type: 'publicKey', + index: false, + }, + { + name: 'foreman', + type: 'publicKey', + index: false, + }, + ], + }, + { + name: 'GaugeEnableEvent', + fields: [ + { + name: 'gauge', + type: 'publicKey', + index: false, + }, + { + name: 'gaugemeister', + type: 'publicKey', + index: false, + }, + { + name: 'foreman', + type: 'publicKey', + index: false, + }, + ], + }, + { + name: 'RevertGaugeVoteEvent', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + index: true, + }, + { + name: 'gauge', + type: 'publicKey', + index: true, + }, + { + name: 'quarry', + type: 'publicKey', + index: true, + }, + { + name: 'gaugeVoterOwner', + type: 'publicKey', + index: true, + }, + { + name: 'votingEpoch', + type: 'u32', + index: false, + }, + { + name: 'subtractedPower', + type: 'u64', + index: false, + }, + { + name: 'updatedAllocatedPower', + type: 'u64', + index: false, + }, + { + name: 'updatedTotalPower', + type: 'u64', + index: false, + }, + ], + }, + { + name: 'SetGaugeVoteEvent', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + index: true, + }, + { + name: 'gauge', + type: 'publicKey', + index: true, + }, + { + name: 'quarry', + type: 'publicKey', + index: true, + }, + { + name: 'gaugeVoterOwner', + type: 'publicKey', + index: true, + }, + { + name: 'voteDelegate', + type: 'publicKey', + index: true, + }, + { + name: 'prevTotalWeight', + type: 'u32', + index: false, + }, + { + name: 'totalWeight', + type: 'u32', + index: false, + }, + { + name: 'weightChangeSeqno', + type: 'u64', + index: false, + }, + ], + }, + { + name: 'PrepareEpochGaugeVoterEvent', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + index: true, + }, + { + name: 'rewarder', + type: 'publicKey', + index: true, + }, + { + name: 'locker', + type: 'publicKey', + index: true, + }, + { + name: 'gaugeVoterOwner', + type: 'publicKey', + index: true, + }, + { + name: 'votingEpoch', + type: 'u32', + index: false, + }, + { + name: 'votingPower', + type: 'u64', + index: false, + }, + { + name: 'weightChangeSeqno', + type: 'u64', + index: false, + }, + ], + }, + { + name: 'ResetEpochGaugeVoterEvent', + fields: [ + { + name: 'gaugemeister', + type: 'publicKey', + index: true, + }, + { + name: 'gaugeVoterOwner', + type: 'publicKey', + index: true, + }, + { + name: 'prevVotingPower', + type: 'u64', + index: false, + }, + { + name: 'votingPower', + type: 'u64', + index: false, + }, + { + name: 'prevWeightChangeSeqno', + type: 'u64', + index: false, + }, + { + name: 'weightChangeSeqno', + type: 'u64', + index: false, + }, + ], + }, + { + name: 'SetGaugemeisterParamsEvent', + fields: [ + { + name: 'prevForeman', + type: 'publicKey', + index: false, + }, + { + name: 'newForeman', + type: 'publicKey', + index: false, + }, + { + name: 'prevEpochDurationSeconds', + type: 'u32', + index: false, + }, + { + name: 'newEpochDurationSeconds', + type: 'u32', + index: false, + }, + ], + }, + { + name: 'SyncGaugeEvent', + fields: [ + { + name: 'gauge', + type: 'publicKey', + index: false, + }, + { + name: 'gaugemeister', + type: 'publicKey', + index: false, + }, + { + name: 'epoch', + type: 'u32', + index: false, + }, + { + name: 'previousShare', + type: 'u64', + index: false, + }, + { + name: 'newShare', + type: 'u64', + index: false, + }, + ], + }, + ], + errors: [ + { + code: 300, + name: 'UnauthorizedNotForeman', + msg: 'You must be the foreman to perform this action.', + }, + { + code: 301, + name: 'GaugeEpochCannotBeZero', + msg: 'Cannot sync gauges at the 0th epoch.', + }, + { + code: 302, + name: 'GaugeWrongEpoch', + msg: 'The gauge is not set to the current epoch.', + }, + { + code: 303, + name: 'NextEpochNotReached', + msg: 'The start time for the next epoch has not yet been reached.', + }, + { + code: 304, + name: 'CannotVoteMustReset', + msg: 'Must set all votes to 0 before changing votes.', + }, + { + code: 305, + name: 'CannotVoteGaugeDisabled', + msg: + 'Cannot vote since gauge is disabled; all you may do is set weight to 0.', + }, + { + code: 306, + name: 'VoteAlreadyCommitted', + msg: 'You have already committed your vote to this gauge.', + }, + { + code: 307, + name: 'CannotCommitGaugeDisabled', + msg: + 'Cannot commit votes since gauge is disabled; all you may do is set weight to 0.', + }, + { + code: 308, + name: 'EpochGaugeNotVoting', + msg: 'Voting on this epoch gauge is closed.', + }, + { + code: 309, + name: 'WeightSeqnoChanged', + msg: + 'Gauge voter voting weights have been modified since you started committing your votes. Please withdraw your votes and try again.', + }, + { + code: 310, + name: 'EpochClosed', + msg: 'You may no longer modify votes for this epoch.', + }, + { + code: 311, + name: 'AllocatedPowerMustBeZero', + msg: + 'You must have zero allocated power in order to reset the epoch gauge.', + }, + ], +} +export const UgaugeErrors = generateErrorMap(UgaugeJSON) diff --git a/tools/sdk/tribeca/idls/govern.ts b/tools/sdk/tribeca/idls/govern.ts new file mode 100644 index 0000000000..61e3c8f23f --- /dev/null +++ b/tools/sdk/tribeca/idls/govern.ts @@ -0,0 +1,1638 @@ +import { generateErrorMap } from '@saberhq/anchor-contrib' + +export type UgovernIDL = { + version: '0.0.0' + name: 'govern' + instructions: [ + { + name: 'createGovernor' + accounts: [ + { + name: 'base' + isMut: false + isSigner: true + }, + { + name: 'governor' + isMut: true + isSigner: false + }, + { + name: 'smartWallet' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + }, + { + name: 'electorate' + type: 'publicKey' + }, + { + name: 'params' + type: { + defined: 'GovernanceParameters' + } + } + ] + }, + { + name: 'createProposal' + accounts: [ + { + name: 'governor' + isMut: true + isSigner: false + }, + { + name: 'proposal' + isMut: true + isSigner: false + }, + { + name: 'proposer' + isMut: false + isSigner: true + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + }, + { + name: 'instructions' + type: { + vec: { + defined: 'ProposalInstruction' + } + } + } + ] + }, + { + name: 'activateProposal' + accounts: [ + { + name: 'governor' + isMut: false + isSigner: false + }, + { + name: 'proposal' + isMut: true + isSigner: false + }, + { + name: 'electorate' + isMut: false + isSigner: true + } + ] + args: [] + }, + { + name: 'cancelProposal' + accounts: [ + { + name: 'governor' + isMut: false + isSigner: false + }, + { + name: 'proposal' + isMut: true + isSigner: false + }, + { + name: 'proposer' + isMut: false + isSigner: true + } + ] + args: [] + }, + { + name: 'queueProposal' + accounts: [ + { + name: 'governor' + isMut: false + isSigner: false + }, + { + name: 'proposal' + isMut: true + isSigner: false + }, + { + name: 'transaction' + isMut: true + isSigner: false + }, + { + name: 'smartWallet' + isMut: true + isSigner: false + }, + { + name: 'payer' + isMut: false + isSigner: true + }, + { + name: 'smartWalletProgram' + isMut: false + isSigner: false + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'txBump' + type: 'u8' + } + ] + }, + { + name: 'newVote' + accounts: [ + { + name: 'proposal' + isMut: false + isSigner: false + }, + { + name: 'vote' + isMut: true + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + }, + { + name: 'voter' + type: 'publicKey' + } + ] + }, + { + name: 'setVote' + accounts: [ + { + name: 'governor' + isMut: false + isSigner: false + }, + { + name: 'proposal' + isMut: true + isSigner: false + }, + { + name: 'vote' + isMut: true + isSigner: false + }, + { + name: 'electorate' + isMut: false + isSigner: true + } + ] + args: [ + { + name: 'side' + type: 'u8' + }, + { + name: 'weight' + type: 'u64' + } + ] + }, + { + name: 'setGovernanceParams' + accounts: [ + { + name: 'governor' + isMut: true + isSigner: false + }, + { + name: 'smartWallet' + isMut: false + isSigner: true + } + ] + args: [ + { + name: 'params' + type: { + defined: 'GovernanceParameters' + } + } + ] + }, + { + name: 'createProposalMeta' + accounts: [ + { + name: 'proposal' + isMut: false + isSigner: false + }, + { + name: 'proposer' + isMut: false + isSigner: true + }, + { + name: 'proposalMeta' + isMut: true + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + }, + { + name: 'title' + type: 'string' + }, + { + name: 'descriptionLink' + type: 'string' + } + ] + } + ] + accounts: [ + { + name: 'Governor' + type: { + kind: 'struct' + fields: [ + { + name: 'base' + type: 'publicKey' + }, + { + name: 'bump' + type: 'u8' + }, + { + name: 'proposalCount' + type: 'u64' + }, + { + name: 'electorate' + type: 'publicKey' + }, + { + name: 'smartWallet' + type: 'publicKey' + }, + { + name: 'params' + type: { + defined: 'GovernanceParameters' + } + } + ] + } + }, + { + name: 'Proposal' + type: { + kind: 'struct' + fields: [ + { + name: 'governor' + type: 'publicKey' + }, + { + name: 'index' + type: 'u64' + }, + { + name: 'bump' + type: 'u8' + }, + { + name: 'proposer' + type: 'publicKey' + }, + { + name: 'quorumVotes' + type: 'u64' + }, + { + name: 'forVotes' + type: 'u64' + }, + { + name: 'againstVotes' + type: 'u64' + }, + { + name: 'abstainVotes' + type: 'u64' + }, + { + name: 'canceledAt' + type: 'i64' + }, + { + name: 'createdAt' + type: 'i64' + }, + { + name: 'activatedAt' + type: 'i64' + }, + { + name: 'votingEndsAt' + type: 'i64' + }, + { + name: 'queuedAt' + type: 'i64' + }, + { + name: 'queuedTransaction' + type: 'publicKey' + }, + { + name: 'instructions' + type: { + vec: { + defined: 'ProposalInstruction' + } + } + } + ] + } + }, + { + name: 'ProposalMeta' + type: { + kind: 'struct' + fields: [ + { + name: 'proposal' + type: 'publicKey' + }, + { + name: 'title' + type: 'string' + }, + { + name: 'descriptionLink' + type: 'string' + } + ] + } + }, + { + name: 'Vote' + type: { + kind: 'struct' + fields: [ + { + name: 'proposal' + type: 'publicKey' + }, + { + name: 'voter' + type: 'publicKey' + }, + { + name: 'bump' + type: 'u8' + }, + { + name: 'side' + type: 'u8' + }, + { + name: 'weight' + type: 'u64' + } + ] + } + } + ] + types: [ + { + name: 'GovernanceParameters' + type: { + kind: 'struct' + fields: [ + { + name: 'votingDelay' + type: 'u64' + }, + { + name: 'votingPeriod' + type: 'u64' + }, + { + name: 'quorumVotes' + type: 'u64' + }, + { + name: 'timelockDelaySeconds' + type: 'i64' + } + ] + } + }, + { + name: 'ProposalInstruction' + type: { + kind: 'struct' + fields: [ + { + name: 'programId' + type: 'publicKey' + }, + { + name: 'keys' + type: { + vec: { + defined: 'ProposalAccountMeta' + } + } + }, + { + name: 'data' + type: 'bytes' + } + ] + } + }, + { + name: 'ProposalAccountMeta' + type: { + kind: 'struct' + fields: [ + { + name: 'pubkey' + type: 'publicKey' + }, + { + name: 'isSigner' + type: 'bool' + }, + { + name: 'isWritable' + type: 'bool' + } + ] + } + }, + { + name: 'ProposalState' + type: { + kind: 'enum' + variants: [ + { + name: 'Draft' + }, + { + name: 'Active' + }, + { + name: 'Canceled' + }, + { + name: 'Defeated' + }, + { + name: 'Succeeded' + }, + { + name: 'Queued' + } + ] + } + }, + { + name: 'VoteSide' + type: { + kind: 'enum' + variants: [ + { + name: 'Pending' + }, + { + name: 'Against' + }, + { + name: 'For' + }, + { + name: 'Abstain' + } + ] + } + } + ] + events: [ + { + name: 'GovernorCreateEvent' + fields: [ + { + name: 'governor' + type: 'publicKey' + index: false + }, + { + name: 'electorate' + type: 'publicKey' + index: false + }, + { + name: 'smartWallet' + type: 'publicKey' + index: false + }, + { + name: 'parameters' + type: { + defined: 'GovernanceParameters' + } + index: false + } + ] + }, + { + name: 'ProposalCreateEvent' + fields: [ + { + name: 'governor' + type: 'publicKey' + index: false + }, + { + name: 'proposal' + type: 'publicKey' + index: false + }, + { + name: 'index' + type: 'u64' + index: false + }, + { + name: 'instructions' + type: { + vec: { + defined: 'ProposalInstruction' + } + } + index: false + } + ] + }, + { + name: 'ProposalActivateEvent' + fields: [ + { + name: 'governor' + type: 'publicKey' + index: false + }, + { + name: 'proposal' + type: 'publicKey' + index: false + }, + { + name: 'votingEndsAt' + type: 'i64' + index: false + } + ] + }, + { + name: 'ProposalCancelEvent' + fields: [ + { + name: 'governor' + type: 'publicKey' + index: false + }, + { + name: 'proposal' + type: 'publicKey' + index: false + } + ] + }, + { + name: 'ProposalQueueEvent' + fields: [ + { + name: 'governor' + type: 'publicKey' + index: false + }, + { + name: 'proposal' + type: 'publicKey' + index: false + }, + { + name: 'transaction' + type: 'publicKey' + index: false + } + ] + }, + { + name: 'VoteSetEvent' + fields: [ + { + name: 'governor' + type: 'publicKey' + index: false + }, + { + name: 'proposal' + type: 'publicKey' + index: false + }, + { + name: 'voter' + type: 'publicKey' + index: false + }, + { + name: 'vote' + type: 'publicKey' + index: false + }, + { + name: 'side' + type: 'u8' + index: false + }, + { + name: 'weight' + type: 'u64' + index: false + } + ] + }, + { + name: 'ProposalMetaCreateEvent' + fields: [ + { + name: 'governor' + type: 'publicKey' + index: false + }, + { + name: 'proposal' + type: 'publicKey' + index: false + }, + { + name: 'title' + type: 'string' + index: false + }, + { + name: 'descriptionLink' + type: 'string' + index: false + } + ] + }, + { + name: 'GovernorSetParamsEvent' + fields: [ + { + name: 'governor' + type: 'publicKey' + index: false + }, + { + name: 'prevParams' + type: { + defined: 'GovernanceParameters' + } + index: false + }, + { + name: 'params' + type: { + defined: 'GovernanceParameters' + } + index: false + } + ] + } + ] + errors: [ + { + code: 300 + name: 'InvalidVoteSide' + msg: 'Invalid vote side.' + }, + { + code: 301 + name: 'GovernorNotFound' + msg: "The owner of the smart wallet doesn't match with current." + }, + { + code: 302 + name: 'VotingDelayNotMet' + msg: 'The proposal cannot be activated since it has not yet passed the voting delay.' + }, + { + code: 303 + name: 'ProposalNotDraft' + msg: 'Only drafts can be canceled.' + }, + { + code: 304 + name: 'ProposalNotActive' + msg: 'The proposal must be active.' + } + ] +} +export const UgovernJSON: UgovernIDL = { + version: '0.0.0', + name: 'govern', + instructions: [ + { + name: 'createGovernor', + accounts: [ + { + name: 'base', + isMut: false, + isSigner: true, + }, + { + name: 'governor', + isMut: true, + isSigner: false, + }, + { + name: 'smartWallet', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'electorate', + type: 'publicKey', + }, + { + name: 'params', + type: { + defined: 'GovernanceParameters', + }, + }, + ], + }, + { + name: 'createProposal', + accounts: [ + { + name: 'governor', + isMut: true, + isSigner: false, + }, + { + name: 'proposal', + isMut: true, + isSigner: false, + }, + { + name: 'proposer', + isMut: false, + isSigner: true, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'instructions', + type: { + vec: { + defined: 'ProposalInstruction', + }, + }, + }, + ], + }, + { + name: 'activateProposal', + accounts: [ + { + name: 'governor', + isMut: false, + isSigner: false, + }, + { + name: 'proposal', + isMut: true, + isSigner: false, + }, + { + name: 'electorate', + isMut: false, + isSigner: true, + }, + ], + args: [], + }, + { + name: 'cancelProposal', + accounts: [ + { + name: 'governor', + isMut: false, + isSigner: false, + }, + { + name: 'proposal', + isMut: true, + isSigner: false, + }, + { + name: 'proposer', + isMut: false, + isSigner: true, + }, + ], + args: [], + }, + { + name: 'queueProposal', + accounts: [ + { + name: 'governor', + isMut: false, + isSigner: false, + }, + { + name: 'proposal', + isMut: true, + isSigner: false, + }, + { + name: 'transaction', + isMut: true, + isSigner: false, + }, + { + name: 'smartWallet', + isMut: true, + isSigner: false, + }, + { + name: 'payer', + isMut: false, + isSigner: true, + }, + { + name: 'smartWalletProgram', + isMut: false, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'txBump', + type: 'u8', + }, + ], + }, + { + name: 'newVote', + accounts: [ + { + name: 'proposal', + isMut: false, + isSigner: false, + }, + { + name: 'vote', + isMut: true, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'voter', + type: 'publicKey', + }, + ], + }, + { + name: 'setVote', + accounts: [ + { + name: 'governor', + isMut: false, + isSigner: false, + }, + { + name: 'proposal', + isMut: true, + isSigner: false, + }, + { + name: 'vote', + isMut: true, + isSigner: false, + }, + { + name: 'electorate', + isMut: false, + isSigner: true, + }, + ], + args: [ + { + name: 'side', + type: 'u8', + }, + { + name: 'weight', + type: 'u64', + }, + ], + }, + { + name: 'setGovernanceParams', + accounts: [ + { + name: 'governor', + isMut: true, + isSigner: false, + }, + { + name: 'smartWallet', + isMut: false, + isSigner: true, + }, + ], + args: [ + { + name: 'params', + type: { + defined: 'GovernanceParameters', + }, + }, + ], + }, + { + name: 'createProposalMeta', + accounts: [ + { + name: 'proposal', + isMut: false, + isSigner: false, + }, + { + name: 'proposer', + isMut: false, + isSigner: true, + }, + { + name: 'proposalMeta', + isMut: true, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'title', + type: 'string', + }, + { + name: 'descriptionLink', + type: 'string', + }, + ], + }, + ], + accounts: [ + { + name: 'Governor', + type: { + kind: 'struct', + fields: [ + { + name: 'base', + type: 'publicKey', + }, + { + name: 'bump', + type: 'u8', + }, + { + name: 'proposalCount', + type: 'u64', + }, + { + name: 'electorate', + type: 'publicKey', + }, + { + name: 'smartWallet', + type: 'publicKey', + }, + { + name: 'params', + type: { + defined: 'GovernanceParameters', + }, + }, + ], + }, + }, + { + name: 'Proposal', + type: { + kind: 'struct', + fields: [ + { + name: 'governor', + type: 'publicKey', + }, + { + name: 'index', + type: 'u64', + }, + { + name: 'bump', + type: 'u8', + }, + { + name: 'proposer', + type: 'publicKey', + }, + { + name: 'quorumVotes', + type: 'u64', + }, + { + name: 'forVotes', + type: 'u64', + }, + { + name: 'againstVotes', + type: 'u64', + }, + { + name: 'abstainVotes', + type: 'u64', + }, + { + name: 'canceledAt', + type: 'i64', + }, + { + name: 'createdAt', + type: 'i64', + }, + { + name: 'activatedAt', + type: 'i64', + }, + { + name: 'votingEndsAt', + type: 'i64', + }, + { + name: 'queuedAt', + type: 'i64', + }, + { + name: 'queuedTransaction', + type: 'publicKey', + }, + { + name: 'instructions', + type: { + vec: { + defined: 'ProposalInstruction', + }, + }, + }, + ], + }, + }, + { + name: 'ProposalMeta', + type: { + kind: 'struct', + fields: [ + { + name: 'proposal', + type: 'publicKey', + }, + { + name: 'title', + type: 'string', + }, + { + name: 'descriptionLink', + type: 'string', + }, + ], + }, + }, + { + name: 'Vote', + type: { + kind: 'struct', + fields: [ + { + name: 'proposal', + type: 'publicKey', + }, + { + name: 'voter', + type: 'publicKey', + }, + { + name: 'bump', + type: 'u8', + }, + { + name: 'side', + type: 'u8', + }, + { + name: 'weight', + type: 'u64', + }, + ], + }, + }, + ], + types: [ + { + name: 'GovernanceParameters', + type: { + kind: 'struct', + fields: [ + { + name: 'votingDelay', + type: 'u64', + }, + { + name: 'votingPeriod', + type: 'u64', + }, + { + name: 'quorumVotes', + type: 'u64', + }, + { + name: 'timelockDelaySeconds', + type: 'i64', + }, + ], + }, + }, + { + name: 'ProposalInstruction', + type: { + kind: 'struct', + fields: [ + { + name: 'programId', + type: 'publicKey', + }, + { + name: 'keys', + type: { + vec: { + defined: 'ProposalAccountMeta', + }, + }, + }, + { + name: 'data', + type: 'bytes', + }, + ], + }, + }, + { + name: 'ProposalAccountMeta', + type: { + kind: 'struct', + fields: [ + { + name: 'pubkey', + type: 'publicKey', + }, + { + name: 'isSigner', + type: 'bool', + }, + { + name: 'isWritable', + type: 'bool', + }, + ], + }, + }, + { + name: 'ProposalState', + type: { + kind: 'enum', + variants: [ + { + name: 'Draft', + }, + { + name: 'Active', + }, + { + name: 'Canceled', + }, + { + name: 'Defeated', + }, + { + name: 'Succeeded', + }, + { + name: 'Queued', + }, + ], + }, + }, + { + name: 'VoteSide', + type: { + kind: 'enum', + variants: [ + { + name: 'Pending', + }, + { + name: 'Against', + }, + { + name: 'For', + }, + { + name: 'Abstain', + }, + ], + }, + }, + ], + events: [ + { + name: 'GovernorCreateEvent', + fields: [ + { + name: 'governor', + type: 'publicKey', + index: false, + }, + { + name: 'electorate', + type: 'publicKey', + index: false, + }, + { + name: 'smartWallet', + type: 'publicKey', + index: false, + }, + { + name: 'parameters', + type: { + defined: 'GovernanceParameters', + }, + index: false, + }, + ], + }, + { + name: 'ProposalCreateEvent', + fields: [ + { + name: 'governor', + type: 'publicKey', + index: false, + }, + { + name: 'proposal', + type: 'publicKey', + index: false, + }, + { + name: 'index', + type: 'u64', + index: false, + }, + { + name: 'instructions', + type: { + vec: { + defined: 'ProposalInstruction', + }, + }, + index: false, + }, + ], + }, + { + name: 'ProposalActivateEvent', + fields: [ + { + name: 'governor', + type: 'publicKey', + index: false, + }, + { + name: 'proposal', + type: 'publicKey', + index: false, + }, + { + name: 'votingEndsAt', + type: 'i64', + index: false, + }, + ], + }, + { + name: 'ProposalCancelEvent', + fields: [ + { + name: 'governor', + type: 'publicKey', + index: false, + }, + { + name: 'proposal', + type: 'publicKey', + index: false, + }, + ], + }, + { + name: 'ProposalQueueEvent', + fields: [ + { + name: 'governor', + type: 'publicKey', + index: false, + }, + { + name: 'proposal', + type: 'publicKey', + index: false, + }, + { + name: 'transaction', + type: 'publicKey', + index: false, + }, + ], + }, + { + name: 'VoteSetEvent', + fields: [ + { + name: 'governor', + type: 'publicKey', + index: false, + }, + { + name: 'proposal', + type: 'publicKey', + index: false, + }, + { + name: 'voter', + type: 'publicKey', + index: false, + }, + { + name: 'vote', + type: 'publicKey', + index: false, + }, + { + name: 'side', + type: 'u8', + index: false, + }, + { + name: 'weight', + type: 'u64', + index: false, + }, + ], + }, + { + name: 'ProposalMetaCreateEvent', + fields: [ + { + name: 'governor', + type: 'publicKey', + index: false, + }, + { + name: 'proposal', + type: 'publicKey', + index: false, + }, + { + name: 'title', + type: 'string', + index: false, + }, + { + name: 'descriptionLink', + type: 'string', + index: false, + }, + ], + }, + { + name: 'GovernorSetParamsEvent', + fields: [ + { + name: 'governor', + type: 'publicKey', + index: false, + }, + { + name: 'prevParams', + type: { + defined: 'GovernanceParameters', + }, + index: false, + }, + { + name: 'params', + type: { + defined: 'GovernanceParameters', + }, + index: false, + }, + ], + }, + ], + errors: [ + { + code: 300, + name: 'InvalidVoteSide', + msg: 'Invalid vote side.', + }, + { + code: 301, + name: 'GovernorNotFound', + msg: "The owner of the smart wallet doesn't match with current.", + }, + { + code: 302, + name: 'VotingDelayNotMet', + msg: + 'The proposal cannot be activated since it has not yet passed the voting delay.', + }, + { + code: 303, + name: 'ProposalNotDraft', + msg: 'Only drafts can be canceled.', + }, + { + code: 304, + name: 'ProposalNotActive', + msg: 'The proposal must be active.', + }, + ], +} +export const UgovernErrors = generateErrorMap(UgovernJSON) diff --git a/tools/sdk/tribeca/idls/locked_voter.ts b/tools/sdk/tribeca/idls/locked_voter.ts new file mode 100644 index 0000000000..6ccbbaf236 --- /dev/null +++ b/tools/sdk/tribeca/idls/locked_voter.ts @@ -0,0 +1,1399 @@ +import { generateErrorMap } from '@saberhq/anchor-contrib' + +export type UlockedUvoterIDL = { + version: '0.0.0' + name: 'locked_voter' + instructions: [ + { + name: 'newLocker' + accounts: [ + { + name: 'base' + isMut: false + isSigner: true + }, + { + name: 'locker' + isMut: true + isSigner: false + }, + { + name: 'tokenMint' + isMut: false + isSigner: false + }, + { + name: 'governor' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + }, + { + name: 'params' + type: { + defined: 'LockerParams' + } + } + ] + }, + { + name: 'newEscrow' + accounts: [ + { + name: 'locker' + isMut: false + isSigner: false + }, + { + name: 'escrow' + isMut: true + isSigner: false + }, + { + name: 'escrowOwner' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + } + ] + }, + { + name: 'lock' + accounts: [ + { + name: 'locker' + isMut: true + isSigner: false + }, + { + name: 'escrow' + isMut: true + isSigner: false + }, + { + name: 'escrowTokens' + isMut: true + isSigner: false + }, + { + name: 'escrowOwner' + isMut: false + isSigner: true + }, + { + name: 'sourceTokens' + isMut: true + isSigner: false + }, + { + name: 'tokenProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'amount' + type: 'u64' + }, + { + name: 'duration' + type: 'i64' + } + ] + }, + { + name: 'exit' + accounts: [ + { + name: 'locker' + isMut: true + isSigner: false + }, + { + name: 'escrow' + isMut: true + isSigner: false + }, + { + name: 'escrowOwner' + isMut: false + isSigner: true + }, + { + name: 'escrowTokens' + isMut: true + isSigner: false + }, + { + name: 'destinationTokens' + isMut: true + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'tokenProgram' + isMut: false + isSigner: false + } + ] + args: [] + }, + { + name: 'activateProposal' + accounts: [ + { + name: 'locker' + isMut: false + isSigner: false + }, + { + name: 'governor' + isMut: false + isSigner: false + }, + { + name: 'proposal' + isMut: true + isSigner: false + }, + { + name: 'escrow' + isMut: false + isSigner: false + }, + { + name: 'escrowOwner' + isMut: false + isSigner: true + }, + { + name: 'governProgram' + isMut: false + isSigner: false + } + ] + args: [] + }, + { + name: 'castVote' + accounts: [ + { + name: 'locker' + isMut: false + isSigner: false + }, + { + name: 'escrow' + isMut: false + isSigner: false + }, + { + name: 'voteDelegate' + isMut: false + isSigner: true + }, + { + name: 'proposal' + isMut: true + isSigner: false + }, + { + name: 'vote' + isMut: true + isSigner: false + }, + { + name: 'governor' + isMut: false + isSigner: false + }, + { + name: 'governProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'side' + type: 'u8' + } + ] + }, + { + name: 'setLockerParams' + accounts: [ + { + name: 'locker' + isMut: true + isSigner: false + }, + { + name: 'governor' + isMut: false + isSigner: false + }, + { + name: 'smartWallet' + isMut: false + isSigner: true + } + ] + args: [ + { + name: 'params' + type: { + defined: 'LockerParams' + } + } + ] + }, + { + name: 'approveProgramLockPrivilege' + accounts: [ + { + name: 'locker' + isMut: false + isSigner: false + }, + { + name: 'whitelistEntry' + isMut: true + isSigner: false + }, + { + name: 'governor' + isMut: false + isSigner: false + }, + { + name: 'smartWallet' + isMut: false + isSigner: true + }, + { + name: 'executableId' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + } + ] + }, + { + name: 'revokeProgramLockPrivilege' + accounts: [ + { + name: 'locker' + isMut: false + isSigner: false + }, + { + name: 'whitelistEntry' + isMut: true + isSigner: false + }, + { + name: 'governor' + isMut: false + isSigner: false + }, + { + name: 'smartWallet' + isMut: false + isSigner: true + }, + { + name: 'executableId' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + } + ] + args: [] + } + ] + accounts: [ + { + name: 'Locker' + type: { + kind: 'struct' + fields: [ + { + name: 'base' + type: 'publicKey' + }, + { + name: 'bump' + type: 'u8' + }, + { + name: 'tokenMint' + type: 'publicKey' + }, + { + name: 'lockedSupply' + type: 'u64' + }, + { + name: 'governor' + type: 'publicKey' + }, + { + name: 'params' + type: { + defined: 'LockerParams' + } + } + ] + } + }, + { + name: 'LockerWhitelistEntry' + type: { + kind: 'struct' + fields: [ + { + name: 'bump' + type: 'u8' + }, + { + name: 'locker' + type: 'publicKey' + }, + { + name: 'programId' + type: 'publicKey' + } + ] + } + }, + { + name: 'Escrow' + type: { + kind: 'struct' + fields: [ + { + name: 'locker' + type: 'publicKey' + }, + { + name: 'owner' + type: 'publicKey' + }, + { + name: 'bump' + type: 'u8' + }, + { + name: 'tokens' + type: 'publicKey' + }, + { + name: 'amount' + type: 'u64' + }, + { + name: 'escrowStartedAt' + type: 'i64' + }, + { + name: 'escrowEndsAt' + type: 'i64' + }, + { + name: 'voteDelegate' + type: 'publicKey' + } + ] + } + } + ] + types: [ + { + name: 'LockerParams' + type: { + kind: 'struct' + fields: [ + { + name: 'whitelistEnabled' + type: 'bool' + }, + { + name: 'maxStakeVoteMultiplier' + type: 'u8' + }, + { + name: 'minStakeDuration' + type: 'u64' + }, + { + name: 'maxStakeDuration' + type: 'u64' + }, + { + name: 'proposalActivationMinVotes' + type: 'u64' + } + ] + } + } + ] + events: [ + { + name: 'NewLockerEvent' + fields: [ + { + name: 'governor' + type: 'publicKey' + index: false + }, + { + name: 'locker' + type: 'publicKey' + index: false + }, + { + name: 'tokenMint' + type: 'publicKey' + index: false + }, + { + name: 'params' + type: { + defined: 'LockerParams' + } + index: false + } + ] + }, + { + name: 'NewEscrowEvent' + fields: [ + { + name: 'escrow' + type: 'publicKey' + index: false + }, + { + name: 'escrowOwner' + type: 'publicKey' + index: false + }, + { + name: 'locker' + type: 'publicKey' + index: false + }, + { + name: 'timestamp' + type: 'i64' + index: false + } + ] + }, + { + name: 'ExitEscrowEvent' + fields: [ + { + name: 'escrowOwner' + type: 'publicKey' + index: false + }, + { + name: 'locker' + type: 'publicKey' + index: false + }, + { + name: 'timestamp' + type: 'i64' + index: false + }, + { + name: 'lockerSupply' + type: 'u64' + index: false + }, + { + name: 'releasedAmount' + type: 'u64' + index: false + } + ] + }, + { + name: 'LockEvent' + fields: [ + { + name: 'locker' + type: 'publicKey' + index: false + }, + { + name: 'escrowOwner' + type: 'publicKey' + index: false + }, + { + name: 'tokenMint' + type: 'publicKey' + index: false + }, + { + name: 'amount' + type: 'u64' + index: false + }, + { + name: 'lockerSupply' + type: 'u64' + index: false + }, + { + name: 'duration' + type: 'i64' + index: false + }, + { + name: 'prevEscrowEndsAt' + type: 'i64' + index: false + }, + { + name: 'nextEscrowEndsAt' + type: 'i64' + index: false + }, + { + name: 'nextEscrowStartedAt' + type: 'i64' + index: false + } + ] + }, + { + name: 'ApproveLockPrivilegeEvent' + fields: [ + { + name: 'locker' + type: 'publicKey' + index: false + }, + { + name: 'programId' + type: 'publicKey' + index: false + }, + { + name: 'timestamp' + type: 'i64' + index: false + } + ] + }, + { + name: 'RevokeLockPrivilegeEvent' + fields: [ + { + name: 'locker' + type: 'publicKey' + index: false + }, + { + name: 'programId' + type: 'publicKey' + index: false + }, + { + name: 'timestamp' + type: 'i64' + index: false + } + ] + }, + { + name: 'LockerSetParamsEvent' + fields: [ + { + name: 'locker' + type: 'publicKey' + index: false + }, + { + name: 'prevParams' + type: { + defined: 'LockerParams' + } + index: false + }, + { + name: 'params' + type: { + defined: 'LockerParams' + } + index: false + } + ] + } + ] + errors: [ + { + code: 300 + name: 'ProgramNotWhitelisted' + msg: 'CPI caller not whitelisted to invoke lock instruction.' + } + ] +} +export const UlockedUvoterJSON: UlockedUvoterIDL = { + version: '0.0.0', + name: 'locked_voter', + instructions: [ + { + name: 'newLocker', + accounts: [ + { + name: 'base', + isMut: false, + isSigner: true, + }, + { + name: 'locker', + isMut: true, + isSigner: false, + }, + { + name: 'tokenMint', + isMut: false, + isSigner: false, + }, + { + name: 'governor', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'params', + type: { + defined: 'LockerParams', + }, + }, + ], + }, + { + name: 'newEscrow', + accounts: [ + { + name: 'locker', + isMut: false, + isSigner: false, + }, + { + name: 'escrow', + isMut: true, + isSigner: false, + }, + { + name: 'escrowOwner', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + ], + }, + { + name: 'lock', + accounts: [ + { + name: 'locker', + isMut: true, + isSigner: false, + }, + { + name: 'escrow', + isMut: true, + isSigner: false, + }, + { + name: 'escrowTokens', + isMut: true, + isSigner: false, + }, + { + name: 'escrowOwner', + isMut: false, + isSigner: true, + }, + { + name: 'sourceTokens', + isMut: true, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'amount', + type: 'u64', + }, + { + name: 'duration', + type: 'i64', + }, + ], + }, + { + name: 'exit', + accounts: [ + { + name: 'locker', + isMut: true, + isSigner: false, + }, + { + name: 'escrow', + isMut: true, + isSigner: false, + }, + { + name: 'escrowOwner', + isMut: false, + isSigner: true, + }, + { + name: 'escrowTokens', + isMut: true, + isSigner: false, + }, + { + name: 'destinationTokens', + isMut: true, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'activateProposal', + accounts: [ + { + name: 'locker', + isMut: false, + isSigner: false, + }, + { + name: 'governor', + isMut: false, + isSigner: false, + }, + { + name: 'proposal', + isMut: true, + isSigner: false, + }, + { + name: 'escrow', + isMut: false, + isSigner: false, + }, + { + name: 'escrowOwner', + isMut: false, + isSigner: true, + }, + { + name: 'governProgram', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'castVote', + accounts: [ + { + name: 'locker', + isMut: false, + isSigner: false, + }, + { + name: 'escrow', + isMut: false, + isSigner: false, + }, + { + name: 'voteDelegate', + isMut: false, + isSigner: true, + }, + { + name: 'proposal', + isMut: true, + isSigner: false, + }, + { + name: 'vote', + isMut: true, + isSigner: false, + }, + { + name: 'governor', + isMut: false, + isSigner: false, + }, + { + name: 'governProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'side', + type: 'u8', + }, + ], + }, + { + name: 'setLockerParams', + accounts: [ + { + name: 'locker', + isMut: true, + isSigner: false, + }, + { + name: 'governor', + isMut: false, + isSigner: false, + }, + { + name: 'smartWallet', + isMut: false, + isSigner: true, + }, + ], + args: [ + { + name: 'params', + type: { + defined: 'LockerParams', + }, + }, + ], + }, + { + name: 'approveProgramLockPrivilege', + accounts: [ + { + name: 'locker', + isMut: false, + isSigner: false, + }, + { + name: 'whitelistEntry', + isMut: true, + isSigner: false, + }, + { + name: 'governor', + isMut: false, + isSigner: false, + }, + { + name: 'smartWallet', + isMut: false, + isSigner: true, + }, + { + name: 'executableId', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + ], + }, + { + name: 'revokeProgramLockPrivilege', + accounts: [ + { + name: 'locker', + isMut: false, + isSigner: false, + }, + { + name: 'whitelistEntry', + isMut: true, + isSigner: false, + }, + { + name: 'governor', + isMut: false, + isSigner: false, + }, + { + name: 'smartWallet', + isMut: false, + isSigner: true, + }, + { + name: 'executableId', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + ], + args: [], + }, + ], + accounts: [ + { + name: 'Locker', + type: { + kind: 'struct', + fields: [ + { + name: 'base', + type: 'publicKey', + }, + { + name: 'bump', + type: 'u8', + }, + { + name: 'tokenMint', + type: 'publicKey', + }, + { + name: 'lockedSupply', + type: 'u64', + }, + { + name: 'governor', + type: 'publicKey', + }, + { + name: 'params', + type: { + defined: 'LockerParams', + }, + }, + ], + }, + }, + { + name: 'LockerWhitelistEntry', + type: { + kind: 'struct', + fields: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'locker', + type: 'publicKey', + }, + { + name: 'programId', + type: 'publicKey', + }, + ], + }, + }, + { + name: 'Escrow', + type: { + kind: 'struct', + fields: [ + { + name: 'locker', + type: 'publicKey', + }, + { + name: 'owner', + type: 'publicKey', + }, + { + name: 'bump', + type: 'u8', + }, + { + name: 'tokens', + type: 'publicKey', + }, + { + name: 'amount', + type: 'u64', + }, + { + name: 'escrowStartedAt', + type: 'i64', + }, + { + name: 'escrowEndsAt', + type: 'i64', + }, + { + name: 'voteDelegate', + type: 'publicKey', + }, + ], + }, + }, + ], + types: [ + { + name: 'LockerParams', + type: { + kind: 'struct', + fields: [ + { + name: 'whitelistEnabled', + type: 'bool', + }, + { + name: 'maxStakeVoteMultiplier', + type: 'u8', + }, + { + name: 'minStakeDuration', + type: 'u64', + }, + { + name: 'maxStakeDuration', + type: 'u64', + }, + { + name: 'proposalActivationMinVotes', + type: 'u64', + }, + ], + }, + }, + ], + events: [ + { + name: 'NewLockerEvent', + fields: [ + { + name: 'governor', + type: 'publicKey', + index: false, + }, + { + name: 'locker', + type: 'publicKey', + index: false, + }, + { + name: 'tokenMint', + type: 'publicKey', + index: false, + }, + { + name: 'params', + type: { + defined: 'LockerParams', + }, + index: false, + }, + ], + }, + { + name: 'NewEscrowEvent', + fields: [ + { + name: 'escrow', + type: 'publicKey', + index: false, + }, + { + name: 'escrowOwner', + type: 'publicKey', + index: false, + }, + { + name: 'locker', + type: 'publicKey', + index: false, + }, + { + name: 'timestamp', + type: 'i64', + index: false, + }, + ], + }, + { + name: 'ExitEscrowEvent', + fields: [ + { + name: 'escrowOwner', + type: 'publicKey', + index: false, + }, + { + name: 'locker', + type: 'publicKey', + index: false, + }, + { + name: 'timestamp', + type: 'i64', + index: false, + }, + { + name: 'lockerSupply', + type: 'u64', + index: false, + }, + { + name: 'releasedAmount', + type: 'u64', + index: false, + }, + ], + }, + { + name: 'LockEvent', + fields: [ + { + name: 'locker', + type: 'publicKey', + index: false, + }, + { + name: 'escrowOwner', + type: 'publicKey', + index: false, + }, + { + name: 'tokenMint', + type: 'publicKey', + index: false, + }, + { + name: 'amount', + type: 'u64', + index: false, + }, + { + name: 'lockerSupply', + type: 'u64', + index: false, + }, + { + name: 'duration', + type: 'i64', + index: false, + }, + { + name: 'prevEscrowEndsAt', + type: 'i64', + index: false, + }, + { + name: 'nextEscrowEndsAt', + type: 'i64', + index: false, + }, + { + name: 'nextEscrowStartedAt', + type: 'i64', + index: false, + }, + ], + }, + { + name: 'ApproveLockPrivilegeEvent', + fields: [ + { + name: 'locker', + type: 'publicKey', + index: false, + }, + { + name: 'programId', + type: 'publicKey', + index: false, + }, + { + name: 'timestamp', + type: 'i64', + index: false, + }, + ], + }, + { + name: 'RevokeLockPrivilegeEvent', + fields: [ + { + name: 'locker', + type: 'publicKey', + index: false, + }, + { + name: 'programId', + type: 'publicKey', + index: false, + }, + { + name: 'timestamp', + type: 'i64', + index: false, + }, + ], + }, + { + name: 'LockerSetParamsEvent', + fields: [ + { + name: 'locker', + type: 'publicKey', + index: false, + }, + { + name: 'prevParams', + type: { + defined: 'LockerParams', + }, + index: false, + }, + { + name: 'params', + type: { + defined: 'LockerParams', + }, + index: false, + }, + ], + }, + ], + errors: [ + { + code: 300, + name: 'ProgramNotWhitelisted', + msg: 'CPI caller not whitelisted to invoke lock instruction.', + }, + ], +} +export const UlockedUvoterErrors = generateErrorMap(UlockedUvoterJSON) diff --git a/tools/sdk/tribeca/idls/quarry_mine.ts b/tools/sdk/tribeca/idls/quarry_mine.ts new file mode 100644 index 0000000000..92bd92bc2f --- /dev/null +++ b/tools/sdk/tribeca/idls/quarry_mine.ts @@ -0,0 +1,1963 @@ +import { generateErrorMap } from '@saberhq/anchor-contrib' + +export type QuarryMineIDL = { + version: '1.11.11' + name: 'quarry_mine' + instructions: [ + { + name: 'newRewarder' + accounts: [ + { + name: 'base' + isMut: false + isSigner: true + }, + { + name: 'rewarder' + isMut: true + isSigner: false + }, + { + name: 'authority' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + }, + { + name: 'unusedClock' + isMut: false + isSigner: false + }, + { + name: 'mintWrapper' + isMut: false + isSigner: false + }, + { + name: 'rewardsTokenMint' + isMut: false + isSigner: false + }, + { + name: 'claimFeeTokenAccount' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + } + ] + }, + { + name: 'setPauseAuthority' + accounts: [ + { + name: 'auth' + accounts: [ + { + name: 'authority' + isMut: false + isSigner: true + }, + { + name: 'rewarder' + isMut: true + isSigner: false + } + ] + }, + { + name: 'pauseAuthority' + isMut: false + isSigner: false + } + ] + args: [] + }, + { + name: 'pause' + accounts: [ + { + name: 'pauseAuthority' + isMut: false + isSigner: true + }, + { + name: 'rewarder' + isMut: true + isSigner: false + } + ] + args: [] + }, + { + name: 'unpause' + accounts: [ + { + name: 'pauseAuthority' + isMut: false + isSigner: true + }, + { + name: 'rewarder' + isMut: true + isSigner: false + } + ] + args: [] + }, + { + name: 'transferAuthority' + accounts: [ + { + name: 'authority' + isMut: false + isSigner: true + }, + { + name: 'rewarder' + isMut: true + isSigner: false + } + ] + args: [ + { + name: 'newAuthority' + type: 'publicKey' + } + ] + }, + { + name: 'acceptAuthority' + accounts: [ + { + name: 'authority' + isMut: false + isSigner: true + }, + { + name: 'rewarder' + isMut: true + isSigner: false + } + ] + args: [] + }, + { + name: 'setAnnualRewards' + accounts: [ + { + name: 'auth' + accounts: [ + { + name: 'authority' + isMut: false + isSigner: true + }, + { + name: 'rewarder' + isMut: true + isSigner: false + } + ] + } + ] + args: [ + { + name: 'newRate' + type: 'u64' + } + ] + }, + { + name: 'createQuarry' + accounts: [ + { + name: 'quarry' + isMut: true + isSigner: false + }, + { + name: 'auth' + accounts: [ + { + name: 'authority' + isMut: false + isSigner: true + }, + { + name: 'rewarder' + isMut: true + isSigner: false + } + ] + }, + { + name: 'tokenMint' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'unusedClock' + isMut: false + isSigner: false + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + } + ] + }, + { + name: 'setRewardsShare' + accounts: [ + { + name: 'auth' + accounts: [ + { + name: 'authority' + isMut: false + isSigner: true + }, + { + name: 'rewarder' + isMut: true + isSigner: false + } + ] + }, + { + name: 'quarry' + isMut: true + isSigner: false + } + ] + args: [ + { + name: 'newShare' + type: 'u64' + } + ] + }, + { + name: 'setFamine' + accounts: [ + { + name: 'auth' + accounts: [ + { + name: 'authority' + isMut: false + isSigner: true + }, + { + name: 'rewarder' + isMut: false + isSigner: false + } + ] + }, + { + name: 'quarry' + isMut: true + isSigner: false + } + ] + args: [ + { + name: 'famineTs' + type: 'i64' + } + ] + }, + { + name: 'updateQuarryRewards' + accounts: [ + { + name: 'quarry' + isMut: true + isSigner: false + }, + { + name: 'rewarder' + isMut: false + isSigner: false + } + ] + args: [] + }, + { + name: 'createMiner' + accounts: [ + { + name: 'authority' + isMut: false + isSigner: true + }, + { + name: 'miner' + isMut: true + isSigner: false + }, + { + name: 'quarry' + isMut: true + isSigner: false + }, + { + name: 'rewarder' + isMut: false + isSigner: false + }, + { + name: 'systemProgram' + isMut: false + isSigner: false + }, + { + name: 'payer' + isMut: true + isSigner: true + }, + { + name: 'tokenMint' + isMut: false + isSigner: false + }, + { + name: 'minerVault' + isMut: false + isSigner: false + }, + { + name: 'tokenProgram' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'bump' + type: 'u8' + } + ] + }, + { + name: 'claimRewards' + accounts: [ + { + name: 'mintWrapper' + isMut: true + isSigner: false + }, + { + name: 'mintWrapperProgram' + isMut: false + isSigner: false + }, + { + name: 'minter' + isMut: true + isSigner: false + }, + { + name: 'rewardsTokenMint' + isMut: true + isSigner: false + }, + { + name: 'rewardsTokenAccount' + isMut: true + isSigner: false + }, + { + name: 'claimFeeTokenAccount' + isMut: true + isSigner: false + }, + { + name: 'stake' + accounts: [ + { + name: 'authority' + isMut: false + isSigner: true + }, + { + name: 'miner' + isMut: true + isSigner: false + }, + { + name: 'quarry' + isMut: true + isSigner: false + }, + { + name: 'unusedMinerVault' + isMut: true + isSigner: false + }, + { + name: 'unusedTokenAccount' + isMut: true + isSigner: false + }, + { + name: 'tokenProgram' + isMut: false + isSigner: false + }, + { + name: 'rewarder' + isMut: false + isSigner: false + } + ] + } + ] + args: [] + }, + { + name: 'stakeTokens' + accounts: [ + { + name: 'authority' + isMut: false + isSigner: true + }, + { + name: 'miner' + isMut: true + isSigner: false + }, + { + name: 'quarry' + isMut: true + isSigner: false + }, + { + name: 'minerVault' + isMut: true + isSigner: false + }, + { + name: 'tokenAccount' + isMut: true + isSigner: false + }, + { + name: 'tokenProgram' + isMut: false + isSigner: false + }, + { + name: 'rewarder' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'amount' + type: 'u64' + } + ] + }, + { + name: 'withdrawTokens' + accounts: [ + { + name: 'authority' + isMut: false + isSigner: true + }, + { + name: 'miner' + isMut: true + isSigner: false + }, + { + name: 'quarry' + isMut: true + isSigner: false + }, + { + name: 'minerVault' + isMut: true + isSigner: false + }, + { + name: 'tokenAccount' + isMut: true + isSigner: false + }, + { + name: 'tokenProgram' + isMut: false + isSigner: false + }, + { + name: 'rewarder' + isMut: false + isSigner: false + } + ] + args: [ + { + name: 'amount' + type: 'u64' + } + ] + }, + { + name: 'extractFees' + accounts: [ + { + name: 'rewarder' + isMut: false + isSigner: false + }, + { + name: 'claimFeeTokenAccount' + isMut: true + isSigner: false + }, + { + name: 'feeToTokenAccount' + isMut: true + isSigner: false + }, + { + name: 'tokenProgram' + isMut: false + isSigner: false + } + ] + args: [] + } + ] + accounts: [ + { + name: 'Rewarder' + type: { + kind: 'struct' + fields: [ + { + name: 'base' + type: 'publicKey' + }, + { + name: 'bump' + type: 'u8' + }, + { + name: 'authority' + type: 'publicKey' + }, + { + name: 'pendingAuthority' + type: 'publicKey' + }, + { + name: 'numQuarries' + type: 'u16' + }, + { + name: 'annualRewardsRate' + type: 'u64' + }, + { + name: 'totalRewardsShares' + type: 'u64' + }, + { + name: 'mintWrapper' + type: 'publicKey' + }, + { + name: 'rewardsTokenMint' + type: 'publicKey' + }, + { + name: 'claimFeeTokenAccount' + type: 'publicKey' + }, + { + name: 'maxClaimFeeMillibps' + type: 'u64' + }, + { + name: 'pauseAuthority' + type: 'publicKey' + }, + { + name: 'isPaused' + type: 'bool' + } + ] + } + }, + { + name: 'Quarry' + type: { + kind: 'struct' + fields: [ + { + name: 'rewarderKey' + type: 'publicKey' + }, + { + name: 'tokenMintKey' + type: 'publicKey' + }, + { + name: 'bump' + type: 'u8' + }, + { + name: 'index' + type: 'u16' + }, + { + name: 'tokenMintDecimals' + type: 'u8' + }, + { + name: 'famineTs' + type: 'i64' + }, + { + name: 'lastUpdateTs' + type: 'i64' + }, + { + name: 'rewardsPerTokenStored' + type: 'u128' + }, + { + name: 'annualRewardsRate' + type: 'u64' + }, + { + name: 'rewardsShare' + type: 'u64' + }, + { + name: 'totalTokensDeposited' + type: 'u64' + }, + { + name: 'numMiners' + type: 'u64' + } + ] + } + }, + { + name: 'Miner' + type: { + kind: 'struct' + fields: [ + { + name: 'quarryKey' + type: 'publicKey' + }, + { + name: 'authority' + type: 'publicKey' + }, + { + name: 'bump' + type: 'u8' + }, + { + name: 'tokenVaultKey' + type: 'publicKey' + }, + { + name: 'rewardsEarned' + type: 'u64' + }, + { + name: 'rewardsPerTokenPaid' + type: 'u128' + }, + { + name: 'balance' + type: 'u64' + }, + { + name: 'index' + type: 'u64' + } + ] + } + } + ] + types: [ + { + name: 'StakeAction' + type: { + kind: 'enum' + variants: [ + { + name: 'Stake' + }, + { + name: 'Withdraw' + } + ] + } + } + ] + events: [ + { + name: 'NewRewarderEvent' + fields: [ + { + name: 'authority' + type: 'publicKey' + index: false + }, + { + name: 'timestamp' + type: 'i64' + index: false + } + ] + }, + { + name: 'ClaimEvent' + fields: [ + { + name: 'authority' + type: 'publicKey' + index: false + }, + { + name: 'stakedToken' + type: 'publicKey' + index: false + }, + { + name: 'rewardsToken' + type: 'publicKey' + index: false + }, + { + name: 'amount' + type: 'u64' + index: false + }, + { + name: 'fees' + type: 'u64' + index: false + }, + { + name: 'timestamp' + type: 'i64' + index: false + } + ] + }, + { + name: 'StakeEvent' + fields: [ + { + name: 'authority' + type: 'publicKey' + index: false + }, + { + name: 'token' + type: 'publicKey' + index: false + }, + { + name: 'amount' + type: 'u64' + index: false + }, + { + name: 'timestamp' + type: 'i64' + index: false + } + ] + }, + { + name: 'WithdrawEvent' + fields: [ + { + name: 'authority' + type: 'publicKey' + index: false + }, + { + name: 'token' + type: 'publicKey' + index: false + }, + { + name: 'amount' + type: 'u64' + index: false + }, + { + name: 'timestamp' + type: 'i64' + index: false + } + ] + }, + { + name: 'RewarderAnnualRewardsUpdateEvent' + fields: [ + { + name: 'previousRate' + type: 'u64' + index: false + }, + { + name: 'newRate' + type: 'u64' + index: false + }, + { + name: 'timestamp' + type: 'i64' + index: false + } + ] + }, + { + name: 'MinerCreateEvent' + fields: [ + { + name: 'authority' + type: 'publicKey' + index: false + }, + { + name: 'quarry' + type: 'publicKey' + index: false + }, + { + name: 'miner' + type: 'publicKey' + index: false + } + ] + }, + { + name: 'QuarryCreateEvent' + fields: [ + { + name: 'tokenMint' + type: 'publicKey' + index: false + }, + { + name: 'timestamp' + type: 'i64' + index: false + } + ] + }, + { + name: 'QuarryRewardsUpdateEvent' + fields: [ + { + name: 'tokenMint' + type: 'publicKey' + index: false + }, + { + name: 'annualRewardsRate' + type: 'u64' + index: false + }, + { + name: 'rewardsShare' + type: 'u64' + index: false + }, + { + name: 'timestamp' + type: 'i64' + index: false + } + ] + } + ] + errors: [ + { + code: 6000 + name: 'Unauthorized' + msg: 'You are not authorized to perform this action.' + }, + { + code: 6001 + name: 'InsufficientBalance' + msg: 'Insufficient staked balance for withdraw request.' + }, + { + code: 6002 + name: 'PendingAuthorityNotSet' + msg: 'Pending authority not set' + }, + { + code: 6003 + name: 'InvalidRewardsShare' + msg: 'Invalid quarry rewards share' + }, + { + code: 6004 + name: 'InsufficientAllowance' + msg: 'Insufficient allowance.' + }, + { + code: 6005 + name: 'NewVaultNotEmpty' + msg: 'New vault not empty.' + }, + { + code: 6006 + name: 'NotEnoughTokens' + msg: 'Not enough tokens.' + }, + { + code: 6007 + name: 'InvalidTimestamp' + msg: 'Invalid timestamp.' + }, + { + code: 6008 + name: 'InvalidMaxClaimFee' + msg: 'Invalid max claim fee.' + }, + { + code: 6009 + name: 'MaxAnnualRewardsRateExceeded' + msg: 'Max annual rewards rate exceeded.' + }, + { + code: 6010 + name: 'Paused' + msg: 'Rewarder is paused.' + }, + { + code: 6011 + name: 'UpperboundExceeded' + msg: "Rewards earned exceeded quarry's upper bound." + } + ] +} +export const QuarryMineJSON: QuarryMineIDL = { + version: '1.11.11', + name: 'quarry_mine', + instructions: [ + { + name: 'newRewarder', + accounts: [ + { + name: 'base', + isMut: false, + isSigner: true, + }, + { + name: 'rewarder', + isMut: true, + isSigner: false, + }, + { + name: 'authority', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + { + name: 'unusedClock', + isMut: false, + isSigner: false, + }, + { + name: 'mintWrapper', + isMut: false, + isSigner: false, + }, + { + name: 'rewardsTokenMint', + isMut: false, + isSigner: false, + }, + { + name: 'claimFeeTokenAccount', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + ], + }, + { + name: 'setPauseAuthority', + accounts: [ + { + name: 'auth', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'rewarder', + isMut: true, + isSigner: false, + }, + ], + }, + { + name: 'pauseAuthority', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'pause', + accounts: [ + { + name: 'pauseAuthority', + isMut: false, + isSigner: true, + }, + { + name: 'rewarder', + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'unpause', + accounts: [ + { + name: 'pauseAuthority', + isMut: false, + isSigner: true, + }, + { + name: 'rewarder', + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'transferAuthority', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'rewarder', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'newAuthority', + type: 'publicKey', + }, + ], + }, + { + name: 'acceptAuthority', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'rewarder', + isMut: true, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'setAnnualRewards', + accounts: [ + { + name: 'auth', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'rewarder', + isMut: true, + isSigner: false, + }, + ], + }, + ], + args: [ + { + name: 'newRate', + type: 'u64', + }, + ], + }, + { + name: 'createQuarry', + accounts: [ + { + name: 'quarry', + isMut: true, + isSigner: false, + }, + { + name: 'auth', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'rewarder', + isMut: true, + isSigner: false, + }, + ], + }, + { + name: 'tokenMint', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'unusedClock', + isMut: false, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + ], + }, + { + name: 'setRewardsShare', + accounts: [ + { + name: 'auth', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'rewarder', + isMut: true, + isSigner: false, + }, + ], + }, + { + name: 'quarry', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'newShare', + type: 'u64', + }, + ], + }, + { + name: 'setFamine', + accounts: [ + { + name: 'auth', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'rewarder', + isMut: false, + isSigner: false, + }, + ], + }, + { + name: 'quarry', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'famineTs', + type: 'i64', + }, + ], + }, + { + name: 'updateQuarryRewards', + accounts: [ + { + name: 'quarry', + isMut: true, + isSigner: false, + }, + { + name: 'rewarder', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'createMiner', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'miner', + isMut: true, + isSigner: false, + }, + { + name: 'quarry', + isMut: true, + isSigner: false, + }, + { + name: 'rewarder', + isMut: false, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'tokenMint', + isMut: false, + isSigner: false, + }, + { + name: 'minerVault', + isMut: false, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'bump', + type: 'u8', + }, + ], + }, + { + name: 'claimRewards', + accounts: [ + { + name: 'mintWrapper', + isMut: true, + isSigner: false, + }, + { + name: 'mintWrapperProgram', + isMut: false, + isSigner: false, + }, + { + name: 'minter', + isMut: true, + isSigner: false, + }, + { + name: 'rewardsTokenMint', + isMut: true, + isSigner: false, + }, + { + name: 'rewardsTokenAccount', + isMut: true, + isSigner: false, + }, + { + name: 'claimFeeTokenAccount', + isMut: true, + isSigner: false, + }, + { + name: 'stake', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'miner', + isMut: true, + isSigner: false, + }, + { + name: 'quarry', + isMut: true, + isSigner: false, + }, + { + name: 'unusedMinerVault', + isMut: true, + isSigner: false, + }, + { + name: 'unusedTokenAccount', + isMut: true, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'rewarder', + isMut: false, + isSigner: false, + }, + ], + }, + ], + args: [], + }, + { + name: 'stakeTokens', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'miner', + isMut: true, + isSigner: false, + }, + { + name: 'quarry', + isMut: true, + isSigner: false, + }, + { + name: 'minerVault', + isMut: true, + isSigner: false, + }, + { + name: 'tokenAccount', + isMut: true, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'rewarder', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'amount', + type: 'u64', + }, + ], + }, + { + name: 'withdrawTokens', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'miner', + isMut: true, + isSigner: false, + }, + { + name: 'quarry', + isMut: true, + isSigner: false, + }, + { + name: 'minerVault', + isMut: true, + isSigner: false, + }, + { + name: 'tokenAccount', + isMut: true, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'rewarder', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'amount', + type: 'u64', + }, + ], + }, + { + name: 'extractFees', + accounts: [ + { + name: 'rewarder', + isMut: false, + isSigner: false, + }, + { + name: 'claimFeeTokenAccount', + isMut: true, + isSigner: false, + }, + { + name: 'feeToTokenAccount', + isMut: true, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + ], + accounts: [ + { + name: 'Rewarder', + type: { + kind: 'struct', + fields: [ + { + name: 'base', + type: 'publicKey', + }, + { + name: 'bump', + type: 'u8', + }, + { + name: 'authority', + type: 'publicKey', + }, + { + name: 'pendingAuthority', + type: 'publicKey', + }, + { + name: 'numQuarries', + type: 'u16', + }, + { + name: 'annualRewardsRate', + type: 'u64', + }, + { + name: 'totalRewardsShares', + type: 'u64', + }, + { + name: 'mintWrapper', + type: 'publicKey', + }, + { + name: 'rewardsTokenMint', + type: 'publicKey', + }, + { + name: 'claimFeeTokenAccount', + type: 'publicKey', + }, + { + name: 'maxClaimFeeMillibps', + type: 'u64', + }, + { + name: 'pauseAuthority', + type: 'publicKey', + }, + { + name: 'isPaused', + type: 'bool', + }, + ], + }, + }, + { + name: 'Quarry', + type: { + kind: 'struct', + fields: [ + { + name: 'rewarderKey', + type: 'publicKey', + }, + { + name: 'tokenMintKey', + type: 'publicKey', + }, + { + name: 'bump', + type: 'u8', + }, + { + name: 'index', + type: 'u16', + }, + { + name: 'tokenMintDecimals', + type: 'u8', + }, + { + name: 'famineTs', + type: 'i64', + }, + { + name: 'lastUpdateTs', + type: 'i64', + }, + { + name: 'rewardsPerTokenStored', + type: 'u128', + }, + { + name: 'annualRewardsRate', + type: 'u64', + }, + { + name: 'rewardsShare', + type: 'u64', + }, + { + name: 'totalTokensDeposited', + type: 'u64', + }, + { + name: 'numMiners', + type: 'u64', + }, + ], + }, + }, + { + name: 'Miner', + type: { + kind: 'struct', + fields: [ + { + name: 'quarryKey', + type: 'publicKey', + }, + { + name: 'authority', + type: 'publicKey', + }, + { + name: 'bump', + type: 'u8', + }, + { + name: 'tokenVaultKey', + type: 'publicKey', + }, + { + name: 'rewardsEarned', + type: 'u64', + }, + { + name: 'rewardsPerTokenPaid', + type: 'u128', + }, + { + name: 'balance', + type: 'u64', + }, + { + name: 'index', + type: 'u64', + }, + ], + }, + }, + ], + types: [ + { + name: 'StakeAction', + type: { + kind: 'enum', + variants: [ + { + name: 'Stake', + }, + { + name: 'Withdraw', + }, + ], + }, + }, + ], + events: [ + { + name: 'NewRewarderEvent', + fields: [ + { + name: 'authority', + type: 'publicKey', + index: false, + }, + { + name: 'timestamp', + type: 'i64', + index: false, + }, + ], + }, + { + name: 'ClaimEvent', + fields: [ + { + name: 'authority', + type: 'publicKey', + index: false, + }, + { + name: 'stakedToken', + type: 'publicKey', + index: false, + }, + { + name: 'rewardsToken', + type: 'publicKey', + index: false, + }, + { + name: 'amount', + type: 'u64', + index: false, + }, + { + name: 'fees', + type: 'u64', + index: false, + }, + { + name: 'timestamp', + type: 'i64', + index: false, + }, + ], + }, + { + name: 'StakeEvent', + fields: [ + { + name: 'authority', + type: 'publicKey', + index: false, + }, + { + name: 'token', + type: 'publicKey', + index: false, + }, + { + name: 'amount', + type: 'u64', + index: false, + }, + { + name: 'timestamp', + type: 'i64', + index: false, + }, + ], + }, + { + name: 'WithdrawEvent', + fields: [ + { + name: 'authority', + type: 'publicKey', + index: false, + }, + { + name: 'token', + type: 'publicKey', + index: false, + }, + { + name: 'amount', + type: 'u64', + index: false, + }, + { + name: 'timestamp', + type: 'i64', + index: false, + }, + ], + }, + { + name: 'RewarderAnnualRewardsUpdateEvent', + fields: [ + { + name: 'previousRate', + type: 'u64', + index: false, + }, + { + name: 'newRate', + type: 'u64', + index: false, + }, + { + name: 'timestamp', + type: 'i64', + index: false, + }, + ], + }, + { + name: 'MinerCreateEvent', + fields: [ + { + name: 'authority', + type: 'publicKey', + index: false, + }, + { + name: 'quarry', + type: 'publicKey', + index: false, + }, + { + name: 'miner', + type: 'publicKey', + index: false, + }, + ], + }, + { + name: 'QuarryCreateEvent', + fields: [ + { + name: 'tokenMint', + type: 'publicKey', + index: false, + }, + { + name: 'timestamp', + type: 'i64', + index: false, + }, + ], + }, + { + name: 'QuarryRewardsUpdateEvent', + fields: [ + { + name: 'tokenMint', + type: 'publicKey', + index: false, + }, + { + name: 'annualRewardsRate', + type: 'u64', + index: false, + }, + { + name: 'rewardsShare', + type: 'u64', + index: false, + }, + { + name: 'timestamp', + type: 'i64', + index: false, + }, + ], + }, + ], + errors: [ + { + code: 6000, + name: 'Unauthorized', + msg: 'You are not authorized to perform this action.', + }, + { + code: 6001, + name: 'InsufficientBalance', + msg: 'Insufficient staked balance for withdraw request.', + }, + { + code: 6002, + name: 'PendingAuthorityNotSet', + msg: 'Pending authority not set', + }, + { + code: 6003, + name: 'InvalidRewardsShare', + msg: 'Invalid quarry rewards share', + }, + { + code: 6004, + name: 'InsufficientAllowance', + msg: 'Insufficient allowance.', + }, + { + code: 6005, + name: 'NewVaultNotEmpty', + msg: 'New vault not empty.', + }, + { + code: 6006, + name: 'NotEnoughTokens', + msg: 'Not enough tokens.', + }, + { + code: 6007, + name: 'InvalidTimestamp', + msg: 'Invalid timestamp.', + }, + { + code: 6008, + name: 'InvalidMaxClaimFee', + msg: 'Invalid max claim fee.', + }, + { + code: 6009, + name: 'MaxAnnualRewardsRateExceeded', + msg: 'Max annual rewards rate exceeded.', + }, + { + code: 6010, + name: 'Paused', + msg: 'Rewarder is paused.', + }, + { + code: 6011, + name: 'UpperboundExceeded', + msg: "Rewards earned exceeded quarry's upper bound.", + }, + ], +} +export const QuarryMineErrors = generateErrorMap(QuarryMineJSON) diff --git a/tools/sdk/tribeca/programs/gauge.ts b/tools/sdk/tribeca/programs/gauge.ts new file mode 100644 index 0000000000..1a59586dba --- /dev/null +++ b/tools/sdk/tribeca/programs/gauge.ts @@ -0,0 +1,30 @@ +import type { AnchorTypes } from '@saberhq/anchor-contrib' + +import type { UgaugeIDL } from '../idls/gauge' + +export * from '../idls/gauge' + +export type GaugeTypes = AnchorTypes< + UgaugeIDL, + { + gaugemeister: GaugemeisterData + gauge: GaugeData + gaugeVoter: GaugeVoterData + gaugeVote: GaugeVoteData + epochGauge: EpochGaugeData + epochGaugeVoter: EpochGaugeVoterData + epochGaugeVote: EpochGaugeVoteData + } +> + +type Accounts = GaugeTypes['Accounts'] + +export type GaugemeisterData = Accounts['Gaugemeister'] +export type GaugeData = Accounts['Gauge'] +export type GaugeVoterData = Accounts['GaugeVoter'] +export type GaugeVoteData = Accounts['GaugeVote'] +export type EpochGaugeData = Accounts['EpochGauge'] +export type EpochGaugeVoterData = Accounts['EpochGaugeVoter'] +export type EpochGaugeVoteData = Accounts['EpochGaugeVote'] + +export type GaugeProgram = GaugeTypes['Program'] diff --git a/tools/sdk/tribeca/programs/govern.ts b/tools/sdk/tribeca/programs/govern.ts new file mode 100644 index 0000000000..dc10f6d9b7 --- /dev/null +++ b/tools/sdk/tribeca/programs/govern.ts @@ -0,0 +1,37 @@ +import type { AnchorTypes } from '@saberhq/anchor-contrib' +import type { AccountMeta } from '@solana/web3.js' + +import type { UgovernIDL } from '../idls/govern' + +export * from '../idls/govern' + +export type GovernTypes = AnchorTypes< + UgovernIDL, + { + governor: GovernorData + proposal: ProposalData + vote: VoteData + proposalMeta: ProposalMetaData + }, + { + ProposalInstruction: ProposalInstruction + ProposalAccountMeta: AccountMeta + GovernanceParameters: GovernanceParameters + } +> + +type Accounts = GovernTypes['Accounts'] +export type GovernorData = Accounts['Governor'] +export type ProposalData = Accounts['Proposal'] +export type VoteData = Accounts['Vote'] +export type ProposalMetaData = Accounts['ProposalMeta'] & { + title: string + descriptionLink: string +} + +export type GovernanceParameters = GovernTypes['Defined']['GovernanceParameters'] +export type ProposalInstruction = GovernTypes['Defined']['ProposalInstruction'] & { + keys: AccountMeta[] +} + +export type GovernProgram = GovernTypes['Program'] diff --git a/tools/sdk/tribeca/programs/index.ts b/tools/sdk/tribeca/programs/index.ts new file mode 100644 index 0000000000..014ba261b2 --- /dev/null +++ b/tools/sdk/tribeca/programs/index.ts @@ -0,0 +1,4 @@ +export * from './govern' +export * from './gauge' +export * from './mine' +export * from './lockedVoter' diff --git a/tools/sdk/tribeca/programs/lockedVoter.ts b/tools/sdk/tribeca/programs/lockedVoter.ts new file mode 100644 index 0000000000..38b050f0db --- /dev/null +++ b/tools/sdk/tribeca/programs/lockedVoter.ts @@ -0,0 +1,22 @@ +import type { AnchorTypes } from '@saberhq/anchor-contrib' + +import type { UlockedUvoterIDL } from '../idls/locked_voter' + +export * from '../idls/locked_voter' + +export type LockedVoterTypes = AnchorTypes< + UlockedUvoterIDL, + { + locker: LockerData + escrow: EscrowData + } +> + +type Accounts = LockedVoterTypes['Accounts'] +export type LockerData = Accounts['Locker'] +export type EscrowData = Accounts['Escrow'] + +export type LockerParams = LockedVoterTypes['Defined']['LockerParams'] + +export type LockedVoterError = LockedVoterTypes['Error'] +export type LockedVoterProgram = LockedVoterTypes['Program'] diff --git a/tools/sdk/tribeca/programs/mine.ts b/tools/sdk/tribeca/programs/mine.ts new file mode 100644 index 0000000000..2bc0f45533 --- /dev/null +++ b/tools/sdk/tribeca/programs/mine.ts @@ -0,0 +1,28 @@ +import type { AnchorTypes } from '@saberhq/anchor-contrib' + +import type { QuarryMineIDL } from '../idls/quarry_mine' + +export * from '../idls/quarry_mine' + +export type MineTypes = AnchorTypes< + QuarryMineIDL, + { + rewarder: RewarderData + quarry: QuarryData + miner: MinerData + } +> + +type Accounts = MineTypes['Accounts'] +export type RewarderData = Accounts['Rewarder'] +export type QuarryData = Accounts['Quarry'] +export type MinerData = Accounts['Miner'] + +export type MineError = MineTypes['Error'] +export type MineEvents = MineTypes['Events'] +export type MineProgram = MineTypes['Program'] + +export type ClaimEvent = MineEvents['ClaimEvent'] +export type StakeEvent = MineEvents['StakeEvent'] +export type WithdrawEvent = MineEvents['WithdrawEvent'] +export type QuarryRewardsUpdateEvent = MineEvents['QuarryRewardsUpdateEvent'] diff --git a/tools/sdk/units.ts b/tools/sdk/units.ts index 3554b1ed61..47ee106f1f 100644 --- a/tools/sdk/units.ts +++ b/tools/sdk/units.ts @@ -23,6 +23,16 @@ export function fmtMintAmount(mint: MintInfo | undefined, mintAmount: BN) { export function getMintDecimalAmount(mint: MintInfo, mintAmount: BN) { return new BigNumber(mintAmount.toString()).shiftedBy(-mint.decimals) } + +export function nativeAmountToFormattedUiAmount( + nativeAmount: BN, + decimals: number +) { + return Number( + new BigNumber(nativeAmount.toString()).shiftedBy(-decimals).toString() + ).toLocaleString() +} + export function getBigNumberAmount(amount: BN | number) { return typeof amount === 'number' ? new BigNumber(amount) diff --git a/utils/splTokens.ts b/utils/splTokens.ts index 9d6176cd3b..1e995700a5 100644 --- a/utils/splTokens.ts +++ b/utils/splTokens.ts @@ -2,6 +2,7 @@ import { PublicKey } from '@solana/web3.js' import SolendConfiguration, { SupportedCollateralMintNames as SolendSupportedCollateralMintNames, } from '@tools/sdk/solend/configuration' +import { abbreviateAddress } from './formatting' export type SplTokenInformation = { name: string @@ -12,6 +13,13 @@ export type SplTokenInformation = { export type SupportedSplTokenNames = | 'USDC' | 'WSOL' + | 'UXP' + | 'UXD' + | 'SBR' + | 'SUNNY' + | 'B30UXP' + | 'Saber UXD-USDC LP' + | 'Saber IOU Token' | SolendSupportedCollateralMintNames export const SPL_TOKENS: { @@ -29,11 +37,61 @@ export const SPL_TOKENS: { decimals: 9, }, + UXD: { + name: 'UXD', + mint: new PublicKey('7kbnvuGBxxj8AG9qp8Scn56muWGaRaFqxg1FsRp3PaFT'), + decimals: 6, + }, + + UXP: { + name: 'UXP', + mint: new PublicKey('UXPhBoR3qG4UCiGNJfV7MqhHyFqKN68g45GoYvAeL2M'), + decimals: 9, + }, + + SBR: { + name: 'SBR', + mint: new PublicKey('Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1'), + decimals: 6, + }, + + SUNNY: { + name: 'SUNNY', + mint: new PublicKey('SUNNYWgPQmFxe9wTZzNK7iPnJ3vYDrkgnxJRJm1s3ag'), + decimals: 6, + }, + + B30UXP: { + name: 'b30UXP (Socean Bonded Tokens)', + mint: new PublicKey('CUP73GUagMSSbT1T3MUqyXGyWCu1rgUPPYVBYdEpPCQF'), + decimals: 9, + }, + + 'Saber UXD-USDC LP': { + name: 'Saber UXD-USDC LP', + mint: new PublicKey('UXDgmqLd1roNYkC4TmJzok61qcM9oKs5foDADiFoCiJ'), + decimals: 6, + }, + + 'Saber IOU Token': { + name: 'SBR - Saber IOU Token (Liquidity Mining Rewards)', + mint: new PublicKey('iouQcQBAiEXe6cKLS85zmZxUqaCqBdeHFpqKoSz615u'), + decimals: 6, + }, + ...SolendConfiguration.getSupportedCollateralMintsInformation(), } as const export type SplTokenUIName = typeof SPL_TOKENS[keyof typeof SPL_TOKENS]['name'] +export function getSplTokenNameByMint(mint: PublicKey): string { + return ( + Object.values(SPL_TOKENS).find( + (splToken) => splToken.mint.toBase58() === mint.toBase58() + )?.name ?? abbreviateAddress(mint) + ) +} + export function getSplTokenMintAddressByUIName( nameToMatch: SplTokenUIName ): PublicKey { diff --git a/yarn.lock b/yarn.lock index bdf55d9f79..8d56c42e1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1875,6 +1875,14 @@ assert "^2.0.0" buffer "^6.0.1" +"@quarryprotocol/quarry-sdk@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@quarryprotocol/quarry-sdk/-/quarry-sdk-3.0.0.tgz#264f480ea2b3e49a25b0ed3ae91bf15f7f0ee442" + integrity sha512-U4bHADUKTCmKwvpSUH3guHfCLXCDDxkhr2W3cGnmwghaN1mWNw1HH9cCpKgFMSpJjoh69spwALxVhDC5lZ42/w== + dependencies: + tiny-invariant "^1.2.0" + tslib "^2.3.1" + "@raydium-io/raydium-sdk@^1.0.1-beta.34": version "1.0.1-beta.46" resolved "https://registry.yarnpkg.com/@raydium-io/raydium-sdk/-/raydium-sdk-1.0.1-beta.46.tgz#9464fc0065d2da33246372174b8f53e62310e4d8" @@ -1975,6 +1983,17 @@ lodash.mapvalues "^4.6.0" tslib "^2.3.1" +"@saberhq/anchor-contrib@^1.12.56": + version "1.12.56" + resolved "https://registry.yarnpkg.com/@saberhq/anchor-contrib/-/anchor-contrib-1.12.56.tgz#f74919e35367bfac847eb1d75fd4441a33cf8194" + integrity sha512-REr3AUT8wO+FdhrURa/ewiYFew/gqWEOcL4oCiZHxCu3I8yoblC8ChhbAG1bydknfwg5o+3NV5YWmQqFEP+ePg== + dependencies: + "@saberhq/solana-contrib" "^1.12.56" + eventemitter3 "^4.0.7" + lodash.camelcase "^4.3.0" + lodash.mapvalues "^4.6.0" + tslib "^2.3.1" + "@saberhq/chai-solana@^1.11.3": version "1.12.48" resolved "https://registry.yarnpkg.com/@saberhq/chai-solana/-/chai-solana-1.12.48.tgz#329bc5b2c3a093c0b3731591c500ae5ce011d163" @@ -2016,6 +2035,41 @@ tiny-invariant "^1.2.0" tslib "^2.3.1" +"@saberhq/solana-contrib@^1.12.55": + version "1.12.55" + resolved "https://registry.yarnpkg.com/@saberhq/solana-contrib/-/solana-contrib-1.12.55.tgz#acf7c5b15b36037e18f0bc6ba6f200636fee97c8" + integrity sha512-Pft3MxOE7Q20IgGEY+gJn6LkCTHNTOT67LDmaAWLb5PnMBSPG+RkAwb6xmPV8IZPOusbGsoY/wP/ksN5eh2Bog== + dependencies: + "@types/promise-retry" "^1.1.3" + "@types/retry" "^0.12.1" + promise-retry "^2.0.1" + retry "^0.13.1" + tiny-invariant "^1.2.0" + tslib "^2.3.1" + +"@saberhq/solana-contrib@^1.12.56": + version "1.12.56" + resolved "https://registry.yarnpkg.com/@saberhq/solana-contrib/-/solana-contrib-1.12.56.tgz#0472ba6960d9993c37808697d7afff5b63e11b14" + integrity sha512-b6Szv+XqJE9pFqQ6QfQluRyvQIXAdmLk/KrZ2Ks/XtQVMX2ZxdBf5mGozGNa8NzuzgeOzj/BQ8akbAazfll5dw== + dependencies: + "@types/promise-retry" "^1.1.3" + "@types/retry" "^0.12.1" + promise-retry "^2.0.1" + retry "^0.13.1" + tiny-invariant "^1.2.0" + tslib "^2.3.1" + +"@saberhq/stableswap-sdk@^1.12.55": + version "1.12.55" + resolved "https://registry.yarnpkg.com/@saberhq/stableswap-sdk/-/stableswap-sdk-1.12.55.tgz#3e05ffc6dc786d8ee4b3f30e8fa0ba73377b3745" + integrity sha512-f/CXFoSaGOZNmC4m4UfIScew6YwX2WY2mTAmt46mChYE7FcklpD62uUukCk887aZSScx1uh3+NujTM3XyV5EEQ== + dependencies: + "@saberhq/solana-contrib" "^1.12.55" + "@saberhq/token-utils" "^1.12.55" + "@solana/buffer-layout" "^4.0.0" + lodash.mapvalues "^4.6.0" + tslib "^2.3.1" + "@saberhq/token-utils@^1.11.3", "@saberhq/token-utils@^1.12.48": version "1.12.48" resolved "https://registry.yarnpkg.com/@saberhq/token-utils/-/token-utils-1.12.48.tgz#2949ed44119583553990fde28aa62b21afc61a15" @@ -2028,6 +2082,18 @@ tiny-invariant "^1.2.0" tslib "^2.3.1" +"@saberhq/token-utils@^1.12.55": + version "1.12.55" + resolved "https://registry.yarnpkg.com/@saberhq/token-utils/-/token-utils-1.12.55.tgz#f18b9f5ef2ca065f4a640c334ea26190f64cf57f" + integrity sha512-2NNhtgps5wQf14WOATwHPr1W79jIny0mXokihdekf1BPCXK9n8Y/4Mz6p/WI8vxR+Tg/DoyeosS0smr5v7RSUw== + dependencies: + "@saberhq/solana-contrib" "^1.12.55" + "@solana/buffer-layout" "^4.0.0" + "@solana/spl-token" "^0.1.8" + "@ubeswap/token-math" "^4.4.4" + tiny-invariant "^1.2.0" + tslib "^2.3.1" + "@saberhq/use-solana@^1.12.26": version "1.12.35" resolved "https://registry.yarnpkg.com/@saberhq/use-solana/-/use-solana-1.12.35.tgz#444c56302ce5e8fc81e546b37f71d7bd2300e2bb" From edc9d5e1830f714a3c1cafa9a6bce8d6c8af17dc Mon Sep 17 00:00:00 2001 From: Sjillen Date: Wed, 6 Apr 2022 14:27:07 +0900 Subject: [PATCH 120/204] Tribeca integration (#54) * add tribeca sdk instructions * add tribeca create epoch gauge instruction form * add Create Escrow Governance instruction form * add Create Gauge Vote instruction form * add CreateGaugeVote + GaugeCommitVote instruction forms * add GaugeRevertVote Instruction form * refactor useInstructionFormBuilder and Tribeca helpers * moar useInstructionFormBuilder refactor + refacto all itxs * add lock tokens instruction form * add last itx forms --- components/instructions/programs/tribeca.tsx | 423 ++++++++++++++++++ hooks/useGovernanceAssets.ts | 55 +++ hooks/useGovernedMultiTypeAccounts.ts | 26 ++ hooks/useInstructionFormBuilder.ts | 38 +- hooks/useTribecaLockerData.ts | 31 -- hooks/useTribecaPrograms.ts | 19 +- package.json | 2 +- .../CreateAssociatedTokenAccount.tsx | 25 +- .../components/instructions/ProposalForm.tsx | 1 - .../Raydium/AddLiquidityToPool.tsx | 50 +-- .../Raydium/RemoveLiquidityFromPool.tsx | 35 +- .../instructions/SelectedInstruction.tsx | 77 ++++ .../Solend/CreateObligationAccount.tsx | 24 +- ...eserveLiquidityAndObligationCollateral.tsx | 32 +- .../Solend/InitObligationAccount.tsx | 20 +- .../instructions/Solend/RefreshObligation.tsx | 24 +- .../instructions/Solend/RefreshReserve.tsx | 22 +- ...ionCollateralAndRedeemReserveLiquidity.tsx | 36 +- .../instructions/Tribeca/CreateEpochGauge.tsx | 91 ++++ .../CreateEscrowGovernanceTokenATA.tsx | 86 ++++ .../instructions/Tribeca/CreateGaugeVote.tsx | 91 ++++ .../instructions/Tribeca/CreateGaugeVoter.tsx | 78 ++++ .../instructions/Tribeca/GaugeCommitVote.tsx | 91 ++++ .../instructions/Tribeca/GaugeRevertVote.tsx | 91 ++++ .../instructions/Tribeca/GaugeSelect.tsx | 36 ++ .../instructions/Tribeca/GovernorSelect.tsx | 34 ++ .../components/instructions/Tribeca/Lock.tsx | 141 ++++++ .../instructions/Tribeca/NewEscrow.tsx | 73 +++ .../Tribeca/PrepareEpochGaugeVoter.tsx | 71 +++ .../Tribeca/ResetEpochGaugeVoter.tsx | 74 +++ .../instructions/Tribeca/SetGaugeVote.tsx | 113 +++++ .../UXD/DepositInsuranceToMangoDepository.tsx | 37 +- .../instructions/UXD/InitializeController.tsx | 35 +- .../UXD/RegisterMangoDepository.tsx | 31 +- .../SetMangoDepositoriesRedeemableSoftCap.tsx | 29 +- .../UXD/SetRedeemGlobalSupplyCap.tsx | 29 +- .../WithdrawInsuranceFromMangoDepository.tsx | 33 +- .../bpfUpgradeableLoader/ProgramUpgrade.tsx | 7 +- public/realms/mainnet-beta.json | 2 +- tools/sdk/tribeca/configurations.ts | 38 +- .../createEpochGaugeInstruction.ts | 41 ++ .../createEscrowSaberATAInstruction.ts | 26 ++ .../createGaugeVoteInstruction.ts | 49 ++ .../createGaugeVoterInstruction.ts | 43 ++ .../gaugeCommitVoteInstruction.ts | 71 +++ .../gaugeRevertVoteInstruction.ts | 78 ++++ .../instructions/gaugeSetVoteInstruction.ts | 46 ++ .../tribeca/instructions/lockInstruction.ts | 83 ++++ .../instructions/newEscrowInstruction.ts | 32 ++ .../prepareEpochGaugeVoterInstruction.ts | 58 +++ .../resetEpochGaugeVoterInstruction.ts | 49 ++ utils/uiTypes/proposalCreationTypes.ts | 70 +++ 52 files changed, 2595 insertions(+), 302 deletions(-) create mode 100644 components/instructions/programs/tribeca.tsx delete mode 100644 hooks/useTribecaLockerData.ts create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateEpochGauge.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateEscrowGovernanceTokenATA.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateGaugeVote.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateGaugeVoter.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeCommitVote.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeRevertVote.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeSelect.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/GovernorSelect.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/Lock.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/NewEscrow.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/PrepareEpochGaugeVoter.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/ResetEpochGaugeVoter.tsx create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Tribeca/SetGaugeVote.tsx create mode 100644 tools/sdk/tribeca/instructions/createEpochGaugeInstruction.ts create mode 100644 tools/sdk/tribeca/instructions/createEscrowSaberATAInstruction.ts create mode 100644 tools/sdk/tribeca/instructions/createGaugeVoteInstruction.ts create mode 100644 tools/sdk/tribeca/instructions/createGaugeVoterInstruction.ts create mode 100644 tools/sdk/tribeca/instructions/gaugeCommitVoteInstruction.ts create mode 100644 tools/sdk/tribeca/instructions/gaugeRevertVoteInstruction.ts create mode 100644 tools/sdk/tribeca/instructions/gaugeSetVoteInstruction.ts create mode 100644 tools/sdk/tribeca/instructions/lockInstruction.ts create mode 100644 tools/sdk/tribeca/instructions/newEscrowInstruction.ts create mode 100644 tools/sdk/tribeca/instructions/prepareEpochGaugeVoterInstruction.ts create mode 100644 tools/sdk/tribeca/instructions/resetEpochGaugeVoterInstruction.ts diff --git a/components/instructions/programs/tribeca.tsx b/components/instructions/programs/tribeca.tsx new file mode 100644 index 0000000000..52c099e7bd --- /dev/null +++ b/components/instructions/programs/tribeca.tsx @@ -0,0 +1,423 @@ +import { nu64, struct, u32, u8 } from 'buffer-layout' +import { AccountMetaData } from '@solana/spl-governance' +import { Connection } from '@solana/web3.js' +import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' + +export const TRIBECA_PROGRAM_INSTRUCTIONS = { + [ATribecaConfiguration.gaugeProgramId.toBase58()]: { + [ATribecaConfiguration.gaugeInstructions.createGaugeVoter]: { + name: 'Tribeca - Create Gauge Voter', + accounts: [ + 'Gauge Voter', + 'Gaugemeister', + 'Escrow', + 'Payer', + 'System Program', + ], + getDataUI: ( + _connection: Connection, + _data: Uint8Array, + accounts: AccountMetaData[] + ) => { + const gaugeVoterMint = accounts[0].pubkey.toString() + + return ( +
+
+ Gauge Voter: + {gaugeVoterMint} +
+
+ ) + }, + }, + + [ATribecaConfiguration.gaugeInstructions.createGaugeVote]: { + name: 'Tribeca - Create Gauge Vote', + accounts: [ + 'Gauge Vote', + 'Gauge Voter', + 'Gauge', + 'Payer', + 'System Program', + ], + getDataUI: ( + _connection: Connection, + _data: Uint8Array, + accounts: AccountMetaData[] + ) => { + const gaugeVoterMint = accounts[1].pubkey.toString() + const gaugeVoteMint = accounts[0].pubkey.toString() + const gaugeMint = accounts[2].pubkey.toString() + + return ( +
+
+ Gauge Voter: + {gaugeVoterMint} +
+ +
+ Gauge Vote: + {gaugeVoteMint} +
+ +
+ Gauge: + {gaugeMint} +
+
+ ) + }, + }, + + [ATribecaConfiguration.gaugeInstructions.setGaugeVote]: { + name: 'Tribeca - Set Gauge Vote', + accounts: [ + 'Gaugemeister', + 'Gauge', + 'Gauge Voter', + 'Gauge Vote', + 'Escrow', + 'Vote Delegate', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + accounts: AccountMetaData[] + ) => { + const gauge = accounts[1].pubkey.toString() + const gaugeVoter = accounts[2].pubkey.toString() + const gaugeVote = accounts[3].pubkey.toString() + + const dataLayout = struct([ + u8('instruction'), + + // ignore 7 bytes + ...Array.from(new Array(7)).map(u8), + + u32('weight'), + ]) + + const { weight } = dataLayout.decode(Buffer.from(data)) as any + + return ( +
+
+ Gauge Voter: + {gaugeVoter} +
+ +
+ Gauge Vote: + {gaugeVote} +
+ +
+ Gauge: + {gauge} +
+ +
+ Weight: + {weight} +
+
+ ) + }, + }, + + [ATribecaConfiguration.gaugeInstructions.prepareEpochGaugeVoter]: { + name: 'Tribeca - Prepare Epoch Gauge Voter', + accounts: [ + 'Gaugemeister', + 'Locker', + 'Escrow', + 'Gauge Voter', + 'Epoch Gauge Voter', + 'Payer', + 'System Program', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + accounts: AccountMetaData[] + ) => { + const gaugeVoter = accounts[3].pubkey.toString() + const epochGaugeVoter = accounts[4].pubkey.toString() + const payer = accounts[5].pubkey.toString() + + return ( +
+
+ Gauge Voter: + {gaugeVoter} +
+ +
+ Epoch Gauge Voter: + {epochGaugeVoter} +
+ +
+ Payer: + {payer} +
+
+ ) + }, + }, + + [ATribecaConfiguration.gaugeInstructions.gaugeCommitVote]: { + name: 'Tribeca - Gauge Commit Vote', + accounts: [ + 'Gaugemeister', + 'Gauge', + 'Gauge Voter', + 'Gauge Vote', + 'Epoch Gauge', + 'Epoch Gauge Voter', + 'Epoch Gauge Vote', + 'Payer', + 'System Program', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + accounts: AccountMetaData[] + ) => { + const gauge = accounts[1].pubkey.toString() + const gaugeVoter = accounts[2].pubkey.toString() + const gaugeVote = accounts[3].pubkey.toString() + const epochGauge = accounts[4].pubkey.toString() + const epochGaugeVoter = accounts[5].pubkey.toString() + const epochGaugeVote = accounts[6].pubkey.toString() + const payer = accounts[7].pubkey.toString() + + return ( +
+
+ Gauge: + {gauge} +
+ +
+ Gauge Voter: + {gaugeVoter} +
+ +
+ Gauge Vote: + {gaugeVote} +
+ +
+ Epoch Gauge: + {epochGauge} +
+ +
+ Epoch Gauge Voter: + {epochGaugeVoter} +
+ +
+ Epoch Gauge Vote: + {epochGaugeVote} +
+ +
+ Payer: + {payer} +
+
+ ) + }, + }, + + [ATribecaConfiguration.gaugeInstructions.gaugeRevertVote]: { + name: 'Tribeca - Gauge Revert Vote', + accounts: [ + 'Gaugemeister', + 'Gauge', + 'Gauge Voter', + 'Gauge Vote', + 'Epoch Gauge', + 'Epoch Gauge Voter', + 'Escrow', + 'Vote Delegate', + 'Epoch Gauge Vote', + 'Payer', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + accounts: AccountMetaData[] + ) => { + const gauge = accounts[1].pubkey.toString() + const gaugeVoter = accounts[2].pubkey.toString() + const gaugeVote = accounts[3].pubkey.toString() + const epochGauge = accounts[4].pubkey.toString() + const epochGaugeVoter = accounts[5].pubkey.toString() + const escrow = accounts[6].pubkey.toString() + const voteDelegate = accounts[7].pubkey.toString() + const epochGaugeVote = accounts[8].pubkey.toString() + const payer = accounts[9].pubkey.toString() + + return ( +
+
+ Gauge: + {gauge} +
+ +
+ Gauge Voter: + {gaugeVoter} +
+ +
+ Gauge Vote: + {gaugeVote} +
+ +
+ Epoch Gauge: + {epochGauge} +
+ +
+ Epoch Gauge Voter: + {epochGaugeVoter} +
+ +
+ Escrow: + {escrow} +
+ +
+ Vote Delegate: + {voteDelegate} +
+ +
+ Epoch Gauge Vote: + {epochGaugeVote} +
+ +
+ Payer: + {payer} +
+
+ ) + }, + }, + + [ATribecaConfiguration.gaugeInstructions.resetEpochGaugeVoter]: { + name: 'Tribeca - Reset Epoch Gauge Voter', + accounts: [ + 'Gaugemeister', + 'Locker', + 'Escrow', + 'Gauge Voter', + 'Epoch Gauge Voter', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + accounts: AccountMetaData[] + ) => { + const escrow = accounts[2].pubkey.toString() + const gaugeVoter = accounts[3].pubkey.toString() + const epochGaugeVoter = accounts[4].pubkey.toString() + + return ( +
+
+ Gauge Voter: + {gaugeVoter} +
+ +
+ Epoch Gauge Voter: + {epochGaugeVoter} +
+ +
+ Escrow: + {escrow} +
+
+ ) + }, + }, + }, + + [ATribecaConfiguration.lockedVoterProgramId.toBase58()]: { + [ATribecaConfiguration.lockedVoterInstructions.newEscrow]: { + name: 'Tribeca - New Escrow', + accounts: ['Locker', 'Escrow', 'Escrow Owner', 'Payer', 'System Program'], + getDataUI: ( + _connection: Connection, + _data: Uint8Array, + accounts: AccountMetaData[] + ) => { + const escrowMint = accounts[1].pubkey.toString() + + return ( +
+
+ Escrow: + {escrowMint} +
+
+ ) + }, + }, + + [ATribecaConfiguration.lockedVoterInstructions.lock]: { + name: 'Tribeca - Lock', + accounts: [ + 'Locker', + 'Escrow', + 'Escrow Tokens', + 'Escrow Owner', + 'Source Tokens', + 'Token Program', + ], + getDataUI: ( + _connection: Connection, + data: Uint8Array, + _accounts: AccountMetaData[] + ) => { + const dataLayout = struct([ + u8('instruction'), + + // ignore 7 bytes + ...Array.from(new Array(7)).map(u8), + + nu64('amount'), + nu64('duration'), + ]) + + const { amount, duration } = dataLayout.decode(Buffer.from(data)) as any + + return ( +
+
+ Native Amount: + {Number(amount).toLocaleString()} +
+ +
+ Duration (seconds): + {Number(duration).toLocaleString()} +
+
+ ) + }, + }, + }, +} diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index e3f830ce5b..eb44fe9588 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -137,6 +137,61 @@ export default function useGovernanceAssets() { ) const availableInstructions = [ + { + id: Instructions.TribecaCreateEpochGauge, + name: 'Tribeca: Create Epoch Gauge', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.TribecaCreateEscrowGovernanceTokenATA, + name: 'Tribeca: Create Escrow Governance Token ATA', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.TribecaCreateGaugeVote, + name: 'Tribeca: Create Gauge Vote', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.TribecaCreateGaugeVoter, + name: 'Tribeca: Create Gauge Voter', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.TribecaGaugeCommitVote, + name: 'Tribeca: Gauge Commit Vote', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.TribecaGaugeRevertVote, + name: 'Tribeca: Gauge Revert Vote', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.TribecaLock, + name: 'Tribeca: Lock Tokens', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.TribecaNewEscrow, + name: 'Tribeca: New Escrow', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.TribecaPrepareEpochGaugeVoter, + name: 'Tribeca: Prepare Epoch Gauge Voter', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.TribecaResetEpochGaugeVoter, + name: 'Tribeca: Reset Epoch Gauge Voter', + isVisible: canUseAnyInstruction, + }, + { + id: Instructions.TribecaGaugeSetVote, + name: 'Tribeca: Set Gauge Vote', + isVisible: canUseAnyInstruction, + }, { id: Instructions.SolendCreateObligationAccount, name: 'Solend: Create Obligation Account', diff --git a/hooks/useGovernedMultiTypeAccounts.ts b/hooks/useGovernedMultiTypeAccounts.ts index acb2f4f23a..1c3596ab5a 100644 --- a/hooks/useGovernedMultiTypeAccounts.ts +++ b/hooks/useGovernedMultiTypeAccounts.ts @@ -1,3 +1,5 @@ +import { GovernanceAccountType } from '@solana/spl-governance' +import { PublicKey } from '@solana/web3.js' import { GovernedMultiTypeAccount } from '@utils/tokens' import { useCallback, useEffect, useState } from 'react' import useGovernanceAssets from './useGovernanceAssets' @@ -62,7 +64,31 @@ export default function useGovernedMultiTypeAccounts() { } }, [getGovernedMultiTypeAccounts]) + const getGovernedAccountPublicKey = useCallback(( + governedAccount: GovernedMultiTypeAccount | undefined, + + // can force the fact to use the owner for SOL Token Governance + forceToUseSolTokenGovernanceOwner?: boolean + ): PublicKey | undefined => { + if (!governedAccount) { + return + } + + const accountType = governedAccount.governance.account.accountType + + if ( + accountType === GovernanceAccountType.ProgramGovernanceV1 || + accountType === GovernanceAccountType.ProgramGovernanceV2 + ) { + if (governedAccount.isSol && !forceToUseSolTokenGovernanceOwner) { + return governedAccount.transferAddress ?? undefined + } + } + return governedAccount.governance.pubkey + }, []) + return { governedMultiTypeAccounts, + getGovernedAccountPublicKey, } } diff --git a/hooks/useInstructionFormBuilder.ts b/hooks/useInstructionFormBuilder.ts index 03f4d96d37..062f1d3c61 100644 --- a/hooks/useInstructionFormBuilder.ts +++ b/hooks/useInstructionFormBuilder.ts @@ -1,7 +1,7 @@ import { useContext, useEffect, useState } from 'react' import * as yup from 'yup' import { serializeInstructionToBase64 } from '@solana/spl-governance' -import { TransactionInstruction } from '@solana/web3.js' +import { Connection, PublicKey, TransactionInstruction } from '@solana/web3.js' import { debounce } from '@utils/debounce' import { isFormValid } from '@utils/formValidation' import { GovernedMultiTypeAccount } from '@utils/tokens' @@ -9,6 +9,8 @@ import { UiInstruction } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from 'pages/dao/[symbol]/proposal/new' import useWalletStore from 'stores/useWalletStore' +import { SignerWalletAdapter } from '@solana/wallet-adapter-base' +import useGovernedMultiTypeAccounts from './useGovernedMultiTypeAccounts' function useInstructionFormBuilder< T extends { @@ -27,11 +29,22 @@ function useInstructionFormBuilder< [key in keyof T]: yup.AnySchema } > - buildInstruction?: () => Promise + buildInstruction?: ({ + form, + connection, + wallet, + governedAccountPubkey, + }: { + form: T + connection: Connection + wallet: SignerWalletAdapter + governedAccountPubkey: PublicKey + }) => Promise }) { const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) const { handleSetInstructions } = useContext(NewProposalContext) + const { getGovernedAccountPublicKey } = useGovernedMultiTypeAccounts() const [form, setForm] = useState(initialFormValues) const [formErrors, setFormErrors] = useState({}) @@ -48,10 +61,14 @@ function useInstructionFormBuilder< } const getInstruction = async (): Promise => { + const governedAccountPubkey = getGovernedAccountPublicKey( + form.governedAccount, + true + ) if ( !wallet?.publicKey || !form.governedAccount?.governance?.account || - //!buildInstruction || + !governedAccountPubkey || !(await validateForm()) ) { return { @@ -60,11 +77,17 @@ function useInstructionFormBuilder< governance: form.governedAccount?.governance, } } - try { return { serializedInstruction: buildInstruction - ? serializeInstructionToBase64(await buildInstruction()) + ? serializeInstructionToBase64( + await buildInstruction({ + form, + connection: connection.current, + wallet, + governedAccountPubkey, + }) + ) : '', isValid: true, governance: form.governedAccount?.governance, @@ -98,11 +121,10 @@ function useInstructionFormBuilder< }, [form]) return { - form, - setForm, - wallet, connection, + wallet, formErrors, + form, handleSetForm, validateForm, } diff --git a/hooks/useTribecaLockerData.ts b/hooks/useTribecaLockerData.ts deleted file mode 100644 index 61111fb38b..0000000000 --- a/hooks/useTribecaLockerData.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { useCallback, useEffect, useState } from 'react' -import { LockerData } from '@tools/sdk/tribeca/programs' -import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' -import useTribecaPrograms from './useTribecaPrograms' - -export default function useTribecaLockerData( - tribecaConfiguration: ATribecaConfiguration | null -) { - const { programs } = useTribecaPrograms(tribecaConfiguration) - - const [lockerData, setLockerData] = useState(null) - - const loadLockerData = useCallback(async (): Promise => { - if (!programs || !tribecaConfiguration) { - return null - } - - return programs.LockedVoter.account.locker.fetch( - tribecaConfiguration.locker - ) - }, [programs, tribecaConfiguration]) - - useEffect(() => { - loadLockerData().then(setLockerData) - }, [loadLockerData]) - - return { - lockerData, - programs, - } -} diff --git a/hooks/useTribecaPrograms.ts b/hooks/useTribecaPrograms.ts index 68f5fe11a8..bba457f8cf 100644 --- a/hooks/useTribecaPrograms.ts +++ b/hooks/useTribecaPrograms.ts @@ -1,14 +1,11 @@ import { Wallet } from '@project-serum/common' -import { - SolanaAugmentedProvider, - SolanaProvider, -} from '@saberhq/solana-contrib' import { useEffect, useState } from 'react' import useWalletStore from 'stores/useWalletStore' import ATribecaConfiguration, { TribecaPrograms, } from '@tools/sdk/tribeca/ATribecaConfiguration' +import { getTribecaPrograms } from '@tools/sdk/tribeca/configurations' export default function useTribecaPrograms( tribecaConfiguration: ATribecaConfiguration | null @@ -23,16 +20,12 @@ export default function useTribecaPrograms( return } - const solanaProvider = SolanaProvider.load({ - connection: connection.current, - sendConnection: connection.current, - wallet: wallet as Wallet, - }) - setPrograms( - tribecaConfiguration.loadPrograms( - new SolanaAugmentedProvider(solanaProvider) - ) + getTribecaPrograms({ + connection: connection.current, + wallet: wallet as Wallet, + config: tribecaConfiguration, + }) ) }, [connection, wallet, tribecaConfiguration]) diff --git a/package.json b/package.json index 189ead00d4..a9e363854c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "license": "MIT", "version": "1.0.0", "scripts": { - "dev": "next dev -p 3001", + "dev": "next dev -p 3000", "build": "next build", "start": "next start", "prepare": "husky install", diff --git a/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx index 13b84461d6..8e354e2310 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React from 'react' import * as yup from 'yup' import Select from '@components/inputs/Select' import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' @@ -8,6 +6,14 @@ import { getSplTokenMintAddressByUIName, SPL_TOKENS } from '@utils/splTokens' import { GovernedMultiTypeAccount } from '@utils/tokens' import { CreateAssociatedTokenAccountForm } from '@utils/uiTypes/proposalCreationTypes' +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + splTokenMintUIName: yup.string().required('SPL Token Mint is required'), +}) + const CreateAssociatedTokenAccount = ({ index, governedAccount, @@ -16,7 +22,6 @@ const CreateAssociatedTokenAccount = ({ governedAccount?: GovernedMultiTypeAccount }) => { const { - wallet, form, formErrors, handleSetForm, @@ -25,20 +30,14 @@ const CreateAssociatedTokenAccount = ({ initialFormValues: { governedAccount, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - splTokenMintUIName: yup.string().required('SPL Token Mint is required'), - }), - buildInstruction: async function () { + schema, + buildInstruction: async function ({ wallet, governedAccountPubkey, form }) { const [tx] = await createAssociatedTokenAccount( // fundingAddress - wallet!.publicKey!, + wallet.publicKey!, // walletAddress - governedAccount!.governance.pubkey, + governedAccountPubkey, // splTokenMintAddress getSplTokenMintAddressByUIName(form.splTokenMintUIName!) diff --git a/pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx b/pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx index 625d404ab5..f6e799efc3 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx @@ -16,7 +16,6 @@ const ProposalForm = ({ itxType: number }) => { const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - const shouldBeGoverned = index !== 0 && governance const [governedAccount, setGovernanceAccount] = useState< diff --git a/pages/dao/[symbol]/proposal/components/instructions/Raydium/AddLiquidityToPool.tsx b/pages/dao/[symbol]/proposal/components/instructions/Raydium/AddLiquidityToPool.tsx index 2a61e71866..a1b4fb5dc8 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Raydium/AddLiquidityToPool.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Raydium/AddLiquidityToPool.tsx @@ -1,25 +1,44 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import React, { useEffect } from 'react' import * as yup from 'yup' import Input from '@components/inputs/Input' import Select from '@components/inputs/Select' import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' - import { createAddLiquidityInstruction } from '@tools/sdk/raydium/createAddLiquidityInstruction' import { getAmountOut, getLiquidityPoolKeysByLabel, } from '@tools/sdk/raydium/helpers' import { liquidityPoolKeysList } from '@tools/sdk/raydium/poolKeys' +import { uiAmountToNativeBN } from '@tools/sdk/units' import { debounce } from '@utils/debounce' import { GovernedMultiTypeAccount } from '@utils/tokens' import { AddLiquidityRaydiumForm } from '@utils/uiTypes/proposalCreationTypes' import SelectOptionList from '../../SelectOptionList' -import { uiAmountToNativeBN } from '@tools/sdk/units' const SLIPPAGE_OPTIONS = [0.5, 1, 2] const FIXED_SIDE_LIST = ['base', 'quote'] +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + liquidityPool: yup.string().required('Liquidity Pool is required'), + baseAmountIn: yup + .number() + .moreThan(0, 'Amount for Base token should be more than 0') + .required('Amount for Base token is required'), + quoteAmountIn: yup + .number() + .moreThan(0, 'Amount for Quote token should be more than 0') + .required('Amount for Quote token is required'), + fixedSide: yup + .string() + .equals(['base', 'quote']) + .required('Fixed Side is required'), + slippage: yup.number().required('Slippage value is required'), +}) + const RaydiumAddLiquidityToPool = ({ index, governedAccount, @@ -39,27 +58,8 @@ const RaydiumAddLiquidityToPool = ({ fixedSide: 'base', slippage: 0.5, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), - liquidityPool: yup.string().required('Liquidity Pool is required'), - baseAmountIn: yup - .number() - .moreThan(0, 'Amount for Base token should be more than 0') - .required('Amount for Base token is required'), - quoteAmountIn: yup - .number() - .moreThan(0, 'Amount for Quote token should be more than 0') - .required('Amount for Quote token is required'), - fixedSide: yup - .string() - .equals(['base', 'quote']) - .required('Fixed Side is required'), - slippage: yup.number().required('Slippage value is required'), - }), - buildInstruction: async function () { + schema, + buildInstruction: async function ({ form, governedAccountPubkey }) { const poolKeys = getLiquidityPoolKeysByLabel(form.liquidityPool!) const [base, quote] = await Promise.all([ connection.current.getTokenSupply(poolKeys.baseMint), @@ -70,7 +70,7 @@ const RaydiumAddLiquidityToPool = ({ uiAmountToNativeBN(form.baseAmountIn!, base.value.decimals), uiAmountToNativeBN(form.quoteAmountIn!, quote.value.decimals), form.fixedSide, - form.governedAccount!.governance.pubkey + governedAccountPubkey ) }, }) diff --git a/pages/dao/[symbol]/proposal/components/instructions/Raydium/RemoveLiquidityFromPool.tsx b/pages/dao/[symbol]/proposal/components/instructions/Raydium/RemoveLiquidityFromPool.tsx index 008d02b4e3..b39063f15c 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Raydium/RemoveLiquidityFromPool.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Raydium/RemoveLiquidityFromPool.tsx @@ -1,23 +1,32 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import React, { useEffect, useState } from 'react' import * as yup from 'yup' import { jsonInfo2PoolKeys } from '@raydium-io/raydium-sdk' -import { PublicKey } from '@solana/web3.js' import Input from '@components/inputs/Input' import Select from '@components/inputs/Select' import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import { createRemoveLiquidityInstruction } from '@tools/sdk/raydium/createRemoveLiquidityInstruction' import { fetchLiquidityPoolData } from '@tools/sdk/raydium/helpers' import { liquidityPoolKeysList } from '@tools/sdk/raydium/poolKeys' +import { uiAmountToNativeBN } from '@tools/sdk/units' import { notify } from '@utils/notifications' +import { GovernedMultiTypeAccount } from '@utils/tokens' import { RemoveLiquidityRaydiumForm } from '@utils/uiTypes/proposalCreationTypes' - import SelectOptionList from '../../SelectOptionList' -import { GovernedMultiTypeAccount } from '@utils/tokens' -import { uiAmountToNativeBN } from '@tools/sdk/units' const POOL_KEYS_OPTIONS = Object.keys(liquidityPoolKeysList) +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + liquidityPool: yup.string().required('Liquidity Pool is required'), + amountIn: yup + .number() + .moreThan(0, 'Amount for LP token should be more than 0') + .required('Amount for LP token is required'), +}) + const RaydiumRemoveLiquidityFromPool = ({ index, governedAccount, @@ -42,24 +51,14 @@ const RaydiumRemoveLiquidityFromPool = ({ liquidityPool: '', amountIn: 0, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), - liquidityPool: yup.string().required('Liquidity Pool is required'), - amountIn: yup - .number() - .moreThan(0, 'Amount for LP token should be more than 0') - .required('Amount for LP token is required'), - }), - buildInstruction: async function () { + schema, + buildInstruction: async function ({ form, governedAccountPubkey }) { if (!lpMintInfo) { throw new Error('missing parameter Liquidity Pool Mint Info') } return createRemoveLiquidityInstruction( - new PublicKey(governedAccount!.governance.pubkey), + governedAccountPubkey, jsonInfo2PoolKeys(liquidityPoolKeysList[form.liquidityPool]), uiAmountToNativeBN(form.amountIn, lpMintInfo.decimals) ) diff --git a/pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx b/pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx index 286c00b9fa..be8de4ad87 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction.tsx @@ -18,6 +18,17 @@ import SolendInitObligationAccount from './Solend/InitObligationAccount' import SolendRefreshObligation from './Solend/RefreshObligation' import SolendRefreshReserve from './Solend/RefreshReserve' import SolendWithdrawObligationCollateralAndRedeemReserveLiquidity from './Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity' +import TribecaCreateEpochGauge from './Tribeca/CreateEpochGauge' +import TribecaCreateEscrowGovernanceTokenATA from './Tribeca/CreateEscrowGovernanceTokenATA' +import TribecaCreateGaugeVote from './Tribeca/CreateGaugeVote' +import TribecaCreateGaugeVoter from './Tribeca/CreateGaugeVoter' +import TribecaGaugeCommitVote from './Tribeca/GaugeCommitVote' +import TribecaGaugeRevertVote from './Tribeca/GaugeRevertVote' +import TribecaLock from './Tribeca/Lock' +import TribecaNewEscrow from './Tribeca/NewEscrow' +import TribecaPrepareEpochGaugeVoter from './Tribeca/PrepareEpochGaugeVoter' +import TribecaResetEpochGaugeVoter from './Tribeca/ResetEpochGaugeVoter' +import TribecaGaugeSetVote from './Tribeca/SetGaugeVote' import UXDDepositInsuranceToMangoDepository from './UXD/DepositInsuranceToMangoDepository' import UXDInitializeController from './UXD/InitializeController' import UXDRegisterMangoDeposiory from './UXD/RegisterMangoDepository' @@ -147,6 +158,72 @@ const SelectedInstruction = ({ governedAccount={governedAccount} /> ) + case Instructions.TribecaCreateEpochGauge: + return ( + + ) + case Instructions.TribecaCreateEscrowGovernanceTokenATA: + return ( + + ) + case Instructions.TribecaCreateGaugeVote: + return ( + + ) + case Instructions.TribecaCreateGaugeVoter: + return ( + + ) + case Instructions.TribecaGaugeCommitVote: + return ( + + ) + case Instructions.TribecaGaugeRevertVote: + return ( + + ) + case Instructions.TribecaLock: + return + case Instructions.TribecaNewEscrow: + return ( + + ) + case Instructions.TribecaPrepareEpochGaugeVoter: + return ( + + ) + case Instructions.TribecaResetEpochGaugeVoter: + return ( + + ) + case Instructions.TribecaGaugeSetVote: + return ( + + ) case Instructions.Mint: return case Instructions.Base64: diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/CreateObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/CreateObligationAccount.tsx index 49a47dcead..54070b6887 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/CreateObligationAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/CreateObligationAccount.tsx @@ -1,12 +1,16 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React from 'react' import * as yup from 'yup' -;('@hooks/useGovernedMultiTypeAccounts') import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import { createObligationAccount } from '@tools/sdk/solend/createObligationAccount' import { GovernedMultiTypeAccount } from '@utils/tokens' import { CreateSolendObligationAccountForm } from '@utils/uiTypes/proposalCreationTypes' +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), +}) + const CreateObligationAccount = ({ index, governedAccount, @@ -15,23 +19,17 @@ const CreateObligationAccount = ({ governedAccount?: GovernedMultiTypeAccount }) => { const { - wallet, connection, } = useInstructionFormBuilder({ index, initialFormValues: { governedAccount, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - }), - buildInstruction: async function () { + schema, + buildInstruction: async function ({ wallet, governedAccountPubkey }) { return createObligationAccount({ - fundingAddress: wallet!.publicKey!, - walletAddress: governedAccount!.governance.pubkey, + fundingAddress: wallet.publicKey!, + walletAddress: governedAccountPubkey, }) }, }) diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx index bdf2e7383c..64fbc3c81e 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/DepositReserveLiquidityAndObligationCollateral.tsx @@ -1,15 +1,25 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React from 'react' import * as yup from 'yup' import Input from '@components/inputs/Input' import Select from '@components/inputs/Select' import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import SolendConfiguration from '@tools/sdk/solend/configuration' import { depositReserveLiquidityAndObligationCollateral } from '@tools/sdk/solend/depositReserveLiquidityAndObligationCollateral' +import { uiAmountToNativeBN } from '@tools/sdk/units' import { GovernedMultiTypeAccount } from '@utils/tokens' import { DepositReserveLiquidityAndObligationCollateralForm } from '@utils/uiTypes/proposalCreationTypes' import SelectOptionList from '../../SelectOptionList' -import { uiAmountToNativeBN } from '@tools/sdk/units' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), + uiAmount: yup + .number() + .moreThan(0, 'Amount should be more than 0') + .required('Amount is required'), +}) const DepositReserveLiquidityAndObligationCollateral = ({ index, @@ -30,20 +40,10 @@ const DepositReserveLiquidityAndObligationCollateral = ({ governedAccount, uiAmount: 0, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - uiAmount: yup - .number() - .moreThan(0, 'Amount should be more than 0') - .required('Amount is required'), - }), - buildInstruction: async function () { + schema, + buildInstruction: async function ({ governedAccountPubkey, form }) { return depositReserveLiquidityAndObligationCollateral({ - obligationOwner: governedAccount!.governance.pubkey, + obligationOwner: governedAccountPubkey, liquidityAmount: uiAmountToNativeBN( form.uiAmount, SolendConfiguration.getSupportedMintInformation(form.mintName!) diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/InitObligationAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/InitObligationAccount.tsx index beb20aa4d3..64c8647538 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/InitObligationAccount.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/InitObligationAccount.tsx @@ -1,11 +1,16 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React from 'react' import * as yup from 'yup' import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import { initObligationAccount } from '@tools/sdk/solend/initObligationAccount' import { GovernedMultiTypeAccount } from '@utils/tokens' import { InitSolendObligationAccountForm } from '@utils/uiTypes/proposalCreationTypes' +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), +}) + const InitObligationAccount = ({ index, governedAccount, @@ -20,15 +25,10 @@ const InitObligationAccount = ({ initialFormValues: { governedAccount, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - }), - buildInstruction: async function () { + schema, + buildInstruction: async function ({ governedAccountPubkey }) { return initObligationAccount({ - obligationOwner: governedAccount!.governance.pubkey, + obligationOwner: governedAccountPubkey, }) }, }) diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx index 6890b9de18..da5c3aa4ed 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshObligation.tsx @@ -1,13 +1,19 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React from 'react' import * as yup from 'yup' import Select from '@components/inputs/Select' import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import { refreshObligation } from '@tools/sdk/solend/refreshObligation' +import { SOLEND_MINT_NAME_OPTIONS } from '@tools/sdk/solend/utils' import { GovernedMultiTypeAccount } from '@utils/tokens' import { RefreshObligationForm } from '@utils/uiTypes/proposalCreationTypes' import SelectOptionList from '../../SelectOptionList' -import { SOLEND_MINT_NAME_OPTIONS } from '@tools/sdk/solend/utils' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), +}) const RefreshObligation = ({ index, @@ -26,16 +32,10 @@ const RefreshObligation = ({ initialFormValues: { governedAccount, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - }), - buildInstruction: async function () { + schema, + buildInstruction: async function ({ form, governedAccountPubkey }) { return refreshObligation({ - obligationOwner: governedAccount!.governance.pubkey, + obligationOwner: governedAccountPubkey, mintNames: [form.mintName!], }) }, diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx index 1faa23dfcc..f289a22c15 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/RefreshReserve.tsx @@ -1,13 +1,19 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React from 'react' import * as yup from 'yup' import Select from '@components/inputs/Select' import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import { refreshReserve } from '@tools/sdk/solend/refreshReserve' +import { SOLEND_MINT_NAME_OPTIONS } from '@tools/sdk/solend/utils' import { GovernedMultiTypeAccount } from '@utils/tokens' import { RefreshReserveForm } from '@utils/uiTypes/proposalCreationTypes' import SelectOptionList from '../../SelectOptionList' -import { SOLEND_MINT_NAME_OPTIONS } from '@tools/sdk/solend/utils' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), +}) const RefreshReserve = ({ index, @@ -26,14 +32,8 @@ const RefreshReserve = ({ initialFormValues: { governedAccount, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - }), - buildInstruction: async function () { + schema, + buildInstruction: async function ({ form }) { return refreshReserve({ mintName: form.mintName!, }) diff --git a/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx b/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx index d21a800fb8..60b746e7c0 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Solend/WithdrawObligationCollateralAndRedeemReserveLiquidity.tsx @@ -1,17 +1,27 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import React from 'react' -import Input from '@components/inputs/Input' import * as yup from 'yup' import { PublicKey } from '@solana/web3.js' +import Input from '@components/inputs/Input' import Select from '@components/inputs/Select' import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import SolendConfiguration from '@tools/sdk/solend/configuration' +import { SOLEND_MINT_NAME_OPTIONS } from '@tools/sdk/solend/utils' import { withdrawObligationCollateralAndRedeemReserveLiquidity } from '@tools/sdk/solend/withdrawObligationCollateralAndRedeemReserveLiquidity' +import { uiAmountToNativeBN } from '@tools/sdk/units' import { GovernedMultiTypeAccount } from '@utils/tokens' import { WithdrawObligationCollateralAndRedeemReserveLiquidityForm } from '@utils/uiTypes/proposalCreationTypes' import SelectOptionList from '../../SelectOptionList' -import { uiAmountToNativeBN } from '@tools/sdk/units' -import { SOLEND_MINT_NAME_OPTIONS } from '@tools/sdk/solend/utils' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + mintName: yup.string().required('Token Name is required'), + uiAmount: yup + .number() + .moreThan(0, 'Amount should be more than 0') + .required('Amount is required'), +}) const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({ index, @@ -32,20 +42,10 @@ const WithdrawObligationCollateralAndRedeemReserveLiquidity = ({ governedAccount, uiAmount: 0, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - mintName: yup.string().required('Token Name is required'), - uiAmount: yup - .number() - .moreThan(0, 'Amount should be more than 0') - .required('Amount is required'), - }), - buildInstruction: async function () { + schema, + buildInstruction: async function ({ form, governedAccountPubkey }) { return withdrawObligationCollateralAndRedeemReserveLiquidity({ - obligationOwner: governedAccount!.governance.pubkey, + obligationOwner: governedAccountPubkey, liquidityAmount: uiAmountToNativeBN( form.uiAmount, SolendConfiguration.getSupportedMintInformation(form.mintName!) diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateEpochGauge.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateEpochGauge.tsx new file mode 100644 index 0000000000..29e8047024 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateEpochGauge.tsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react' +import * as yup from 'yup' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import useTribecaGauge from '@hooks/useTribecaGauge' +import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' +import createEpochGaugeInstruction from '@tools/sdk/tribeca/instructions/createEpochGaugeInstruction' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { TribecaCreateEpochGaugeForm } from '@utils/uiTypes/proposalCreationTypes' +import GaugeSelect from './GaugeSelect' +import GovernorSelect from './GovernorSelect' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + gaugeName: yup.string().required('Gauge is required'), +}) + +const CreateEpochGauge = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const [ + tribecaConfiguration, + setTribecaConfiguration, + ] = useState(null) + + const { gauges, programs } = useTribecaGauge(tribecaConfiguration) + const { + connection, + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema, + buildInstruction: async function ({ form, wallet, governedAccountPubkey }) { + if ( + !programs || + !gauges || + !gauges[form.gaugeName!] || + !tribecaConfiguration + ) { + throw new Error('Error initializing Tribeca configuration') + } + + return createEpochGaugeInstruction({ + programs, + tribecaConfiguration, + gauge: gauges[form.gaugeName!].mint, + payer: wallet!.publicKey!, + authority: governedAccountPubkey, + }) + }, + }) + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + return ( + <> + + + + handleSetForm({ + value, + propertyName: 'gaugeName', + }) + } + error={formErrors['gaugeName']} + /> + + ) +} + +export default CreateEpochGauge diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateEscrowGovernanceTokenATA.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateEscrowGovernanceTokenATA.tsx new file mode 100644 index 0000000000..f912cff3c9 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateEscrowGovernanceTokenATA.tsx @@ -0,0 +1,86 @@ +import * as yup from 'yup' +import { Wallet } from '@project-serum/common' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import { + getTribecaLocker, + getTribecaPrograms, +} from '@tools/sdk/tribeca/configurations' +import { createEscrowATAInstruction } from '@tools/sdk/tribeca/instructions/createEscrowSaberATAInstruction' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { TribecaCreateEscrowGovernanceTokenATAForm } from '@utils/uiTypes/proposalCreationTypes' +import GovernorSelect from './GovernorSelect' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + tribecaConfiguration: yup + .object() + .nullable() + .required('Tribeca Configuration governor is required'), +}) + +const CreateEscrowGovernanceATA = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const { + connection, + form, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + tribecaConfiguration: null, + }, + schema, + buildInstruction: async function ({ + form, + connection, + wallet, + governedAccountPubkey, + }) { + const programs = getTribecaPrograms({ + connection, + wallet: wallet as Wallet, + config: form.tribecaConfiguration!, + }) + const lockerData = await getTribecaLocker({ + config: form.tribecaConfiguration!, + programs, + }) + // FIXME: does not pass this check without refreshing the form + if (!lockerData) { + throw new Error('Error initializing Tribeca configuration') + } + + return createEscrowATAInstruction({ + tribecaConfiguration: form.tribecaConfiguration!, + lockerData, + payer: wallet.publicKey!, + authority: governedAccountPubkey, + }) + }, + }) + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + return ( + + handleSetForm({ propertyName: 'tribecaConfiguration', value }) + } + /> + ) +} + +export default CreateEscrowGovernanceATA diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateGaugeVote.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateGaugeVote.tsx new file mode 100644 index 0000000000..3bb80bbc86 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateGaugeVote.tsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react' +import * as yup from 'yup' +import { TribecaCreateGaugeVoteForm } from '@utils/uiTypes/proposalCreationTypes' +import useTribecaGauge from '@hooks/useTribecaGauge' +import GaugeSelect from './GaugeSelect' +import GovernorSelect from './GovernorSelect' +import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import { createGaugeVoteInstruction } from '@tools/sdk/tribeca/instructions/createGaugeVoteInstruction' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + gaugeName: yup.string().required('Gauge is required'), +}) + +const CreateGaugeVote = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const [ + tribecaConfiguration, + setTribecaConfiguration, + ] = useState(null) + + const { gauges, programs } = useTribecaGauge(tribecaConfiguration) + const { + connection, + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema, + buildInstruction: async function ({ form, wallet, governedAccountPubkey }) { + if ( + !programs || + !gauges || + !gauges[form.gaugeName!] || + !tribecaConfiguration + ) { + throw new Error('Error initializing Tribeca configuration') + } + + return createGaugeVoteInstruction({ + tribecaConfiguration, + programs, + gauge: gauges[form.gaugeName!].mint, + payer: wallet.publicKey!, + authority: governedAccountPubkey, + }) + }, + }) + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + return ( + <> + + + + handleSetForm({ + value, + propertyName: 'gaugeName', + }) + } + error={formErrors['gaugeName']} + /> + + ) +} + +export default CreateGaugeVote diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateGaugeVoter.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateGaugeVoter.tsx new file mode 100644 index 0000000000..12deeb075e --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/CreateGaugeVoter.tsx @@ -0,0 +1,78 @@ +import * as yup from 'yup' +import { Wallet } from '@project-serum/common' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import { getTribecaPrograms } from '@tools/sdk/tribeca/configurations' +import { createGaugeVoterInstruction } from '@tools/sdk/tribeca/instructions/createGaugeVoterInstruction' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { TribecaCreateGaugeVoterForm } from '@utils/uiTypes/proposalCreationTypes' +import GovernorSelect from './GovernorSelect' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + tribecaConfiguration: yup + .object() + .nullable() + .required('Tribeca Configuration governor is required'), +}) + +const CreateGaugeVoter = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const { + connection, + form, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + tribecaConfiguration: null, + }, + schema, + buildInstruction: async function ({ + wallet, + connection, + form, + governedAccountPubkey, + }) { + const programs = getTribecaPrograms({ + wallet: wallet as Wallet, + connection, + config: form.tribecaConfiguration!, + }) + if (!programs) { + throw new Error('Error initializing Tribeca configuration') + } + + return createGaugeVoterInstruction({ + tribecaConfiguration: form.tribecaConfiguration!, + programs, + payer: wallet.publicKey!, + authority: governedAccountPubkey, + }) + }, + }) + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + return ( + + handleSetForm({ propertyName: 'tribecaConfiguration', value }) + } + /> + ) +} + +export default CreateGaugeVoter diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeCommitVote.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeCommitVote.tsx new file mode 100644 index 0000000000..3df8a68e2e --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeCommitVote.tsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react' +import * as yup from 'yup' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import useTribecaGauge from '@hooks/useTribecaGauge' +import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' +import { gaugeCommitVoteInstruction } from '@tools/sdk/tribeca/instructions/gaugeCommitVoteInstruction' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { TribecaGaugeCommitVoteForm } from '@utils/uiTypes/proposalCreationTypes' +import GaugeSelect from './GaugeSelect' +import GovernorSelect from './GovernorSelect' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + gaugeName: yup.string().required('Gauge is required'), +}) + +const GaugeCommitVote = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const [ + tribecaConfiguration, + setTribecaConfiguration, + ] = useState(null) + + const { gauges, programs } = useTribecaGauge(tribecaConfiguration) + const { + connection, + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema, + buildInstruction: async function ({ wallet, form, governedAccountPubkey }) { + if ( + !programs || + !gauges || + !gauges[form.gaugeName!] || + !tribecaConfiguration + ) { + throw new Error('Error initializing Tribeca configuration') + } + + return gaugeCommitVoteInstruction({ + tribecaConfiguration, + programs, + gauge: gauges[form.gaugeName!].mint, + payer: wallet.publicKey!, + authority: governedAccountPubkey, + }) + }, + }) + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + return ( + <> + + + + handleSetForm({ + value, + propertyName: 'gaugeName', + }) + } + error={formErrors['gaugeName']} + /> + + ) +} + +export default GaugeCommitVote diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeRevertVote.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeRevertVote.tsx new file mode 100644 index 0000000000..cd492b9583 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeRevertVote.tsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react' +import * as yup from 'yup' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import useTribecaGauge from '@hooks/useTribecaGauge' +import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' +import { gaugeRevertVoteInstruction } from '@tools/sdk/tribeca/instructions/gaugeRevertVoteInstruction' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { TribecaGaugeRevertVoteForm } from '@utils/uiTypes/proposalCreationTypes' +import GaugeSelect from './GaugeSelect' +import GovernorSelect from './GovernorSelect' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + gaugeName: yup.string().required('Gauge is required'), +}) + +const GaugeRevertVote = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const [ + tribecaConfiguration, + setTribecaConfiguration, + ] = useState(null) + + const { gauges, programs } = useTribecaGauge(tribecaConfiguration) + const { + connection, + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema, + buildInstruction: async function ({ wallet, form, governedAccountPubkey }) { + if ( + !programs || + !gauges || + !gauges[form.gaugeName!] || + !tribecaConfiguration + ) { + throw new Error('Error initializing Tribeca configuration') + } + + return gaugeRevertVoteInstruction({ + programs, + gauge: gauges[form.gaugeName!].mint, + authority: governedAccountPubkey, + payer: wallet.publicKey!, + tribecaConfiguration, + }) + }, + }) + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + return ( + <> + + + + handleSetForm({ + value, + propertyName: 'gaugeName', + }) + } + error={formErrors['gaugeName']} + /> + + ) +} + +export default GaugeRevertVote diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeSelect.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeSelect.tsx new file mode 100644 index 0000000000..eaa80738c2 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GaugeSelect.tsx @@ -0,0 +1,36 @@ +import Select from '@components/inputs/Select' +import { GaugeInfos } from '@tools/sdk/tribeca/ATribecaConfiguration' + +const GaugeSelect = ({ + gauges, + value, + error, + onChange, +}: { + gauges: GaugeInfos | null + value?: string + error?: string + onChange: (v: string) => void +}) => { + return ( + + ) +} + +export default GaugeSelect diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GovernorSelect.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GovernorSelect.tsx new file mode 100644 index 0000000000..060d502fd3 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/GovernorSelect.tsx @@ -0,0 +1,34 @@ +import Select from '@components/inputs/Select' +import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' +import { + configurations as tribecaConfigurations, + getConfigurationByName, +} from '@tools/sdk/tribeca/configurations' +import SelectOptionList from '../../SelectOptionList' + +const configurationList = Object.values(tribecaConfigurations || {}).map( + (configuration) => configuration.name +) + +const GovernorSelect = ({ + tribecaConfiguration, + setTribecaConfiguration, +}: { + tribecaConfiguration: ATribecaConfiguration | null + setTribecaConfiguration: (v: ATribecaConfiguration | null) => void +}) => { + return ( + + ) +} + +export default GovernorSelect diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/Lock.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/Lock.tsx new file mode 100644 index 0000000000..4f209556b0 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/Lock.tsx @@ -0,0 +1,141 @@ +import { BigNumber } from 'bignumber.js' +import * as yup from 'yup' +import { BN } from '@project-serum/anchor' +import { Wallet } from '@project-serum/common' +import Input from '@components/inputs/Input' +import Select from '@components/inputs/Select' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import { + getTribecaLocker, + getTribecaPrograms, +} from '@tools/sdk/tribeca/configurations' +import { lockInstruction } from '@tools/sdk/tribeca/instructions/lockInstruction' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { TribecaLockForm } from '@utils/uiTypes/proposalCreationTypes' +import SelectOptionList from '../../SelectOptionList' +import GovernorSelect from './GovernorSelect' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + tribecaConfiguration: yup + .object() + .nullable() + .required('Tribeca Configuration Governoris required'), + uiAmount: yup + .number() + .moreThan(0, 'Amount should be more than 0') + .required('Amount is required'), + durationSeconds: yup.number().required('Duration is required'), +}) + +const Lock = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const { + form, + handleSetForm, + formErrors, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + tribecaConfiguration: null, + uiAmount: 0, + durationSeconds: 0, + }, + schema, + buildInstruction: async function ({ + connection, + wallet, + form, + governedAccountPubkey, + }) { + const programs = getTribecaPrograms({ + connection, + wallet: wallet as Wallet, + config: form.tribecaConfiguration!, + }) + const lockerData = await getTribecaLocker({ + programs, + config: form.tribecaConfiguration!, + }) + + return lockInstruction({ + tribecaConfiguration: form.tribecaConfiguration!, + programs, + lockerData, + authority: governedAccountPubkey, + amount: new BN( + new BigNumber(form.uiAmount!) + .shiftedBy(form.tribecaConfiguration!.token.decimals) + .toNumber() + ), + durationSeconds: new BN(form.durationSeconds * 60 * 60 * 24 * 365), + }) + }, + }) + + return ( + <> + + handleSetForm({ value, propertyName: 'tribecaConfiguration' }) + } + /> + + + handleSetForm({ + value: evt.target.value, + propertyName: 'uiAmount', + }) + } + error={formErrors['uiAmount']} + /> + + + + {/* + handleSetForm({ + value: evt.target.value, + propertyName: 'durationSeconds', + }) + } + error={formErrors['durationSeconds']} + /> */} + + ) +} + +export default Lock diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/NewEscrow.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/NewEscrow.tsx new file mode 100644 index 0000000000..ec9345c546 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/NewEscrow.tsx @@ -0,0 +1,73 @@ +import * as yup from 'yup' +import { Wallet } from '@project-serum/common' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import { getTribecaPrograms } from '@tools/sdk/tribeca/configurations' +import { newEscrowInstruction } from '@tools/sdk/tribeca/instructions/newEscrowInstruction' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { TribecaNewEscrowForm } from '@utils/uiTypes/proposalCreationTypes' + +import useWalletStore from 'stores/useWalletStore' + +import GovernorSelect from './GovernorSelect' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + tribecaConfiguration: yup + .object() + .nullable() + .required('Tribeca Configuration Governor is required'), +}) + +const NewEscrow = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const connection = useWalletStore((s) => s.connection) + const { + form, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + tribecaConfiguration: null, + }, + schema, + buildInstruction: async function ({ wallet, form, governedAccountPubkey }) { + const programs = getTribecaPrograms({ + connection: connection.current, + wallet: wallet as Wallet, + config: form.tribecaConfiguration!, + }) + + return newEscrowInstruction({ + tribecaConfiguration: form.tribecaConfiguration!, + programs, + payer: wallet.publicKey!, + authority: governedAccountPubkey, + }) + }, + }) + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + return ( + + handleSetForm({ value, propertyName: 'tribecaConfiguration' }) + } + /> + ) +} + +export default NewEscrow diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/PrepareEpochGaugeVoter.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/PrepareEpochGaugeVoter.tsx new file mode 100644 index 0000000000..6b4f19c2bc --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/PrepareEpochGaugeVoter.tsx @@ -0,0 +1,71 @@ +import * as yup from 'yup' +import { Wallet } from '@project-serum/common' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import { getTribecaPrograms } from '@tools/sdk/tribeca/configurations' +import { prepareEpochGaugeVoterInstruction } from '@tools/sdk/tribeca/instructions/prepareEpochGaugeVoterInstruction' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { TribecaPrepareEpochGaugeVoterForm } from '@utils/uiTypes/proposalCreationTypes' +import useWalletStore from 'stores/useWalletStore' +import GovernorSelect from './GovernorSelect' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + tribecaConfiguration: yup + .object() + .nullable() + .required('Tribeca Configuration Governor is required'), +}) + +const PrepareEpochGaugeVoter = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const connection = useWalletStore((s) => s.connection) + const { + form, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + tribecaConfiguration: null, + }, + schema, + buildInstruction: async function ({ wallet, form, governedAccountPubkey }) { + const programs = getTribecaPrograms({ + connection: connection.current, + wallet: wallet as Wallet, + config: form.tribecaConfiguration!, + }) + + return prepareEpochGaugeVoterInstruction({ + tribecaConfiguration: form.tribecaConfiguration!, + programs, + payer: wallet.publicKey!, + authority: governedAccountPubkey, + }) + }, + }) + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + return ( + + handleSetForm({ value, propertyName: 'tribecaConfiguration' }) + } + /> + ) +} + +export default PrepareEpochGaugeVoter diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/ResetEpochGaugeVoter.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/ResetEpochGaugeVoter.tsx new file mode 100644 index 0000000000..1d7a030528 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/ResetEpochGaugeVoter.tsx @@ -0,0 +1,74 @@ +import * as yup from 'yup' +import { Wallet } from '@project-serum/common' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import { getTribecaPrograms } from '@tools/sdk/tribeca/configurations' +import { resetEpochGaugeVoterInstruction } from '@tools/sdk/tribeca/instructions/resetEpochGaugeVoterInstruction' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { TribecaResetEpochGaugeVoterForm } from '@utils/uiTypes/proposalCreationTypes' +import useWalletStore from 'stores/useWalletStore' +import GovernorSelect from './GovernorSelect' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + tribecaConfiguration: yup + .object() + .nullable() + .required('Tribeca Configuration Governor is required'), +}) + +const ResetEpochGaugeVoter = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const { + form, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + tribecaConfiguration: null, + }, + schema, + buildInstruction: async function ({ + connection, + wallet, + form, + governedAccountPubkey, + }) { + const programs = getTribecaPrograms({ + connection, + wallet: wallet as Wallet, + config: form.tribecaConfiguration!, + }) + return resetEpochGaugeVoterInstruction({ + programs, + authority: governedAccountPubkey, + tribecaConfiguration: form.tribecaConfiguration!, + }) + }, + }) + const connection = useWalletStore((s) => s.connection) + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + return ( + + handleSetForm({ value, propertyName: 'tribecaConfiguration' }) + } + /> + ) +} + +export default ResetEpochGaugeVoter diff --git a/pages/dao/[symbol]/proposal/components/instructions/Tribeca/SetGaugeVote.tsx b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/SetGaugeVote.tsx new file mode 100644 index 0000000000..7e19919297 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Tribeca/SetGaugeVote.tsx @@ -0,0 +1,113 @@ +import React, { useState } from 'react' +import * as yup from 'yup' +import Input from '@components/inputs/Input' +import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' +import useTribecaGauge from '@hooks/useTribecaGauge' +import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' +import { gaugeSetVoteInstruction } from '@tools/sdk/tribeca/instructions/gaugeSetVoteInstruction' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { TribecaGaugeSetVoteForm } from '@utils/uiTypes/proposalCreationTypes' +import useWalletStore from 'stores/useWalletStore' +import GaugeSelect from './GaugeSelect' +import GovernorSelect from './GovernorSelect' + +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + gaugeName: yup.string().required('Gauge is required'), + weight: yup + .number() + .min(0, 'Weight should be equals or more than 0') + .required('Weight is required'), +}) + +const SetGaugeVote = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const connection = useWalletStore((s) => s.connection) + + const [ + tribecaConfiguration, + setTribecaConfiguration, + ] = useState(null) + + const { gauges, programs } = useTribecaGauge(tribecaConfiguration) + + const { + form, + handleSetForm, + formErrors, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + }, + schema, + buildInstruction: async function ({ form, governedAccountPubkey }) { + if ( + !programs || + !gauges || + !gauges[form.gaugeName!] || + !tribecaConfiguration + ) { + throw new Error('Error initializing Tribeca configuration') + } + + return gaugeSetVoteInstruction({ + tribecaConfiguration, + weight: form.weight!, + programs, + gauge: gauges[form.gaugeName!].mint, + authority: governedAccountPubkey, + }) + }, + }) + + // Hardcoded gate used to be clear about what cluster is supported for now + if (connection.cluster !== 'mainnet') { + return <>This instruction does not support {connection.cluster} + } + + return ( + <> + + + + handleSetForm({ + value, + propertyName: 'gaugeName', + }) + } + error={formErrors['gaugeName']} + /> + + + handleSetForm({ + value: evt.target.value, + propertyName: 'weight', + }) + } + error={formErrors['weight']} + /> + + ) +} + +export default SetGaugeVote diff --git a/pages/dao/[symbol]/proposal/components/instructions/UXD/DepositInsuranceToMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/DepositInsuranceToMangoDepository.tsx index a2e307d58a..9e2cbae84f 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/UXD/DepositInsuranceToMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/DepositInsuranceToMangoDepository.tsx @@ -11,6 +11,19 @@ import { GovernedMultiTypeAccount } from '@utils/tokens' import { DepositInsuranceToMangoDepositoryForm } from '@utils/uiTypes/proposalCreationTypes' import SelectOptionList from '../../SelectOptionList' +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governance account is required'), + collateralName: yup.string().required('Collateral Name address is required'), + insuranceName: yup.string().required('Insurance Name address is required'), + insuranceDepositedAmount: yup + .number() + .moreThan(0, 'Insurance Deposited amount should be more than 0') + .required('Insurance Deposited amount is required'), +}) + const UXDDepositInsuranceToMangoDepository = ({ index, governedAccount, @@ -29,30 +42,12 @@ const UXDDepositInsuranceToMangoDepository = ({ governedAccount, insuranceDepositedAmount: 0, }, - schema: yup.object().shape({ - collateralName: yup - .string() - .required('Collateral Name address is required'), - insuranceName: yup - .string() - .required('Insurance Name address is required'), - insuranceDepositedAmount: yup - .number() - .moreThan(0, 'Insurance Deposited amount should be more than 0') - .required('Insurance Deposited amount is required'), - governedAccount: yup - .object() - .nullable() - .required('Governance account is required'), - }), - buildInstruction: async function () { - if (!governedAccount?.governance?.account) { - throw new Error('Governance must be a Program Account Governance') - } + schema, + buildInstruction: async function ({ form, governedAccountPubkey }) { return createDepositInsuranceToMangoDepositoryInstruction( connection, form.governedAccount!.governance!.account.governedAccount, - form.governedAccount!.governance!.pubkey, + governedAccountPubkey, form.collateralName!, form.insuranceName!, form.insuranceDepositedAmount diff --git a/pages/dao/[symbol]/proposal/components/instructions/UXD/InitializeController.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/InitializeController.tsx index 99590dc505..d829d6167a 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/UXD/InitializeController.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/InitializeController.tsx @@ -1,11 +1,22 @@ import * as yup from 'yup' -import { PublicKey } from '@solana/web3.js' import Input from '@components/inputs/Input' import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import createInitializeControllerInstruction from '@tools/sdk/uxdProtocol/createInitializeControllerInstruction' import { GovernedMultiTypeAccount } from '@utils/tokens' import { InitializeControllerForm } from '@utils/uiTypes/proposalCreationTypes' +const schema = yup.object().shape({ + mintDecimals: yup + .number() + .min(0, 'Mint decimals cannot be less than 0') + .max(9, 'Mint decimals cannot be more than 9') + .required('Mint Decimals is required'), + governedAccount: yup + .object() + .nullable() + .required('Governance account is required'), +}) + const InitializeController = ({ index, governedAccount, @@ -14,7 +25,6 @@ const InitializeController = ({ governedAccount?: GovernedMultiTypeAccount }) => { const { - wallet, form, formErrors, handleSetForm, @@ -24,26 +34,13 @@ const InitializeController = ({ governedAccount, mintDecimals: 0, }, - schema: yup.object().shape({ - mintDecimals: yup - .number() - .min(0, 'Mint decimals cannot be less than 0') - .max(9, 'Mint decimals cannot be more than 9') - .required('Mint Decimals is required'), - governedAccount: yup - .object() - .nullable() - .required('Governance account is required'), - }), - buildInstruction: async function () { - if (!governedAccount?.governance?.account) { - throw new Error('Governance must be a Program Account Governance') - } + schema, + buildInstruction: async function ({ form, wallet, governedAccountPubkey }) { return createInitializeControllerInstruction( form.governedAccount!.governance.account.governedAccount, form.mintDecimals, - form.governedAccount!.governance.pubkey, - new PublicKey(wallet!.publicKey!.toBase58()) + governedAccountPubkey, + wallet.publicKey! ) }, }) diff --git a/pages/dao/[symbol]/proposal/components/instructions/UXD/RegisterMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/RegisterMangoDepository.tsx index 8d22ba0ac4..25be909e0f 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/UXD/RegisterMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/RegisterMangoDepository.tsx @@ -1,5 +1,4 @@ import * as yup from 'yup' -import { PublicKey } from '@solana/web3.js' import Select from '@components/inputs/Select' import useInstructionFormBuilder from '@hooks/useInstructionFormBuilder' import createRegisterMangoDepositoryInstruction from '@tools/sdk/uxdProtocol/createRegisterMangoDepositoryInstruction' @@ -11,6 +10,15 @@ import { GovernedMultiTypeAccount } from '@utils/tokens' import { RegisterMangoDepositoryForm } from '@utils/uiTypes/proposalCreationTypes' import SelectOptionList from '../../SelectOptionList' +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governance account is required'), + collateralName: yup.string().required('Valid Collateral name is required'), + insuranceName: yup.string().required('Valid Insurance name is required'), +}) + const RegisterMangoDepository = ({ index, governedAccount, @@ -20,7 +28,6 @@ const RegisterMangoDepository = ({ }) => { const { connection, - wallet, form, formErrors, handleSetForm, @@ -29,25 +36,13 @@ const RegisterMangoDepository = ({ initialFormValues: { governedAccount, }, - schema: yup.object().shape({ - collateralName: yup - .string() - .required('Valid Collateral name is required'), - insuranceName: yup.string().required('Valid Insurance name is required'), - governedAccount: yup - .object() - .nullable() - .required('Governance account is required'), - }), - buildInstruction: async function () { - if (!governedAccount?.governance?.account) { - throw new Error('Governance must be a Program Account Governance') - } + schema, + buildInstruction: async function ({ form, wallet, governedAccountPubkey }) { return createRegisterMangoDepositoryInstruction( connection, form.governedAccount!.governance.account.governedAccount, - form.governedAccount!.governance.pubkey, - new PublicKey(wallet!.publicKey!.toBase58()), + governedAccountPubkey, + wallet.publicKey!, form.collateralName!, form.insuranceName! ) diff --git a/pages/dao/[symbol]/proposal/components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap.tsx index 6cefdd9687..7c080a7632 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetMangoDepositoriesRedeemableSoftCap.tsx @@ -5,6 +5,17 @@ import createSetMangoDepositoriesRedeemableSoftCapInstruction from '@tools/sdk/u import { GovernedMultiTypeAccount } from '@utils/tokens' import { SetMangoDepositoriesRedeemableSoftCapForm } from '@utils/uiTypes/proposalCreationTypes' +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governance account is required'), + softCap: yup + .number() + .moreThan(0, 'Redeemable soft cap should be more than 0') + .required('Redeemable soft cap is required'), +}) + const SetMangoDepositoriesRedeemableSoftCap = ({ index, governedAccount, @@ -22,24 +33,12 @@ const SetMangoDepositoriesRedeemableSoftCap = ({ governedAccount, softCap: 0, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governance account is required'), - softCap: yup - .number() - .moreThan(0, 'Redeemable soft cap should be more than 0') - .required('Redeemable soft cap is required'), - }), - buildInstruction: async function () { - if (!governedAccount?.governance?.account) { - throw new Error('Governance must be a Program Account Governance') - } + schema, + buildInstruction: async function ({ form, governedAccountPubkey }) { return createSetMangoDepositoriesRedeemableSoftCapInstruction( form.governedAccount!.governance.account.governedAccount, form.softCap, - form.governedAccount!.governance.pubkey + governedAccountPubkey ) }, }) diff --git a/pages/dao/[symbol]/proposal/components/instructions/UXD/SetRedeemGlobalSupplyCap.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetRedeemGlobalSupplyCap.tsx index abe364ddc4..9f64d8fcd8 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/UXD/SetRedeemGlobalSupplyCap.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/SetRedeemGlobalSupplyCap.tsx @@ -6,6 +6,17 @@ import { SetRedeemableGlobalSupplyCapForm } from '@utils/uiTypes/proposalCreatio import Input from '@components/inputs/Input' import { GovernedMultiTypeAccount } from '@utils/tokens' +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + supplyCap: yup + .number() + .moreThan(0, 'Redeemable global supply cap should be more than 0') + .required('Redeemable global supply cap is required'), +}) + const SetRedeemGlobalSupplyCap = ({ index, governedAccount, @@ -23,24 +34,12 @@ const SetRedeemGlobalSupplyCap = ({ governedAccount, supplyCap: 0, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), - supplyCap: yup - .number() - .moreThan(0, 'Redeemable global supply cap should be more than 0') - .required('Redeemable global supply cap is required'), - }), - buildInstruction: async function () { - if (!governedAccount?.governance?.account) { - throw new Error('Governance must be a Program Account Governance') - } + schema, + buildInstruction: async function ({ form, governedAccountPubkey }) { return createSetRedeemableGlobalSupplyCapInstruction( form.governedAccount!.governance.account.governedAccount, form.supplyCap, - form.governedAccount!.governance.pubkey + governedAccountPubkey ) }, }) diff --git a/pages/dao/[symbol]/proposal/components/instructions/UXD/WithdrawInsuranceFromMangoDepository.tsx b/pages/dao/[symbol]/proposal/components/instructions/UXD/WithdrawInsuranceFromMangoDepository.tsx index 16911b8c09..741fa3036f 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/UXD/WithdrawInsuranceFromMangoDepository.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/UXD/WithdrawInsuranceFromMangoDepository.tsx @@ -11,6 +11,19 @@ import { GovernedMultiTypeAccount } from '@utils/tokens' import { WithdrawInsuranceFromMangoDepositoryForm } from '@utils/uiTypes/proposalCreationTypes' import SelectOptionList from '../../SelectOptionList' +const schema = yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Program governed account is required'), + collateralName: yup.string().required('Collateral Name is required'), + insuranceName: yup.string().required('Insurance Name is required'), + insuranceWithdrawnAmount: yup + .number() + .moreThan(0, 'Insurance Withdrawn amount should be more than 0') + .required('Insurance Withdrawn amount is required'), +}) + const WithdrawInsuranceFromMangoDepository = ({ index, governedAccount, @@ -29,26 +42,12 @@ const WithdrawInsuranceFromMangoDepository = ({ governedAccount, insuranceWithdrawnAmount: 0, }, - schema: yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Program governed account is required'), - collateralName: yup.string().required('Collateral Name is required'), - insuranceName: yup.string().required('Insurance Name is required'), - insuranceWithdrawnAmount: yup - .number() - .moreThan(0, 'Insurance Withdrawn amount should be more than 0') - .required('Insurance Withdrawn amount is required'), - }), - buildInstruction: async function () { - if (!governedAccount?.governance?.account) { - throw new Error('Governance must be a Program Account Governance') - } + schema, + buildInstruction: async function ({ form, governedAccountPubkey }) { return createWithdrawInsuranceFromMangoDepositoryInstruction( connection, form.governedAccount!.governance.account.governedAccount, - form.governedAccount!.governance.pubkey, + governedAccountPubkey, form.collateralName!, form.insuranceName!, form.insuranceWithdrawnAmount diff --git a/pages/dao/[symbol]/proposal/components/instructions/bpfUpgradeableLoader/ProgramUpgrade.tsx b/pages/dao/[symbol]/proposal/components/instructions/bpfUpgradeableLoader/ProgramUpgrade.tsx index 10dee3f684..c16a383fd1 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/bpfUpgradeableLoader/ProgramUpgrade.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/bpfUpgradeableLoader/ProgramUpgrade.tsx @@ -21,7 +21,6 @@ const ProgramUpgrade = ({ }) => { const connection = useWalletStore((s) => s.connection) const { - wallet, form, formErrors, handleSetForm, @@ -82,19 +81,19 @@ const ProgramUpgrade = ({ } ), }), - buildInstruction: async function () { + buildInstruction: async function ({ form, wallet, governedAccountPubkey }) { if (!governedAccount?.governance?.account) { throw new Error('Governance must be a Program Account Governance') } const bufferSpillAddress = form.bufferSpillAddress ? new PublicKey(form.bufferSpillAddress) - : wallet!.publicKey! + : wallet.publicKey! return createUpgradeInstruction( form.governedAccount!.governance.account.governedAccount, new PublicKey(form.bufferAddress!), - form.governedAccount!.governance.pubkey, + governedAccountPubkey, bufferSpillAddress ) }, diff --git a/public/realms/mainnet-beta.json b/public/realms/mainnet-beta.json index 7522758b58..8a4fb78d39 100644 --- a/public/realms/mainnet-beta.json +++ b/public/realms/mainnet-beta.json @@ -344,7 +344,7 @@ }, { "symbol": "UXP", - "displayName": "UXDProtocol", + "displayName": "UXD Protocol", "programId": "GovER5Lthms3bLBqWub97yVrMmEogzX7xNjdXpPPCVZw", "realmId": "GBXLYo4ycRNfzuzYeudu6y2ng4afNeW14WcpM2E4JJSL", "website": "https://uxd.fi", diff --git a/tools/sdk/tribeca/configurations.ts b/tools/sdk/tribeca/configurations.ts index b6a06717cd..9feb6edf1c 100644 --- a/tools/sdk/tribeca/configurations.ts +++ b/tools/sdk/tribeca/configurations.ts @@ -1,6 +1,11 @@ -import { PublicKey } from '@solana/web3.js' +import { Wallet } from '@project-serum/common' +import { + SolanaAugmentedProvider, + SolanaProvider, +} from '@saberhq/solana-contrib' +import { Connection, PublicKey } from '@solana/web3.js' import { SPL_TOKENS } from '@utils/splTokens' -import ATribecaConfiguration from './ATribecaConfiguration' +import ATribecaConfiguration, { TribecaPrograms } from './ATribecaConfiguration' class SaberTribecaConfiguration extends ATribecaConfiguration { public readonly locker = new PublicKey( @@ -37,3 +42,32 @@ export function getConfigurationByName( ) ?? null ) } + +export function getTribecaPrograms({ + connection, + wallet, + config, +}: { + connection: Connection + wallet: Wallet + config: ATribecaConfiguration +}) { + return config.loadPrograms( + new SolanaAugmentedProvider( + SolanaProvider.init({ + connection, + wallet, + }) + ) + ) +} + +export async function getTribecaLocker({ + programs, + config, +}: { + programs: TribecaPrograms + config: ATribecaConfiguration +}) { + return programs.LockedVoter.account.locker.fetch(config.locker) +} diff --git a/tools/sdk/tribeca/instructions/createEpochGaugeInstruction.ts b/tools/sdk/tribeca/instructions/createEpochGaugeInstruction.ts new file mode 100644 index 0000000000..bc9b993d71 --- /dev/null +++ b/tools/sdk/tribeca/instructions/createEpochGaugeInstruction.ts @@ -0,0 +1,41 @@ +import { + PublicKey, + SystemProgram, + TransactionInstruction, +} from '@solana/web3.js' +import ATribecaConfiguration from '../ATribecaConfiguration' +import { TribecaPrograms } from '../ATribecaConfiguration' + +export async function createEpochGaugeInstruction({ + programs, + gauge, + payer, + tribecaConfiguration, +}: { + programs: TribecaPrograms + gauge: PublicKey + authority: PublicKey + payer: PublicKey + tribecaConfiguration: ATribecaConfiguration +}): Promise { + const { currentRewardsEpoch } = await tribecaConfiguration.fetchGaugemeister( + programs.Gauge + ) + + const votingEpoch = currentRewardsEpoch + 1 + + const [epochGauge, bump] = await tribecaConfiguration.findEpochGaugeAddress( + gauge, + votingEpoch + ) + + return programs.Gauge.instruction.createEpochGauge(bump, votingEpoch, { + accounts: { + epochGauge, + gauge, + payer, + systemProgram: SystemProgram.programId, + }, + }) +} +export default createEpochGaugeInstruction diff --git a/tools/sdk/tribeca/instructions/createEscrowSaberATAInstruction.ts b/tools/sdk/tribeca/instructions/createEscrowSaberATAInstruction.ts new file mode 100644 index 0000000000..b6023876f6 --- /dev/null +++ b/tools/sdk/tribeca/instructions/createEscrowSaberATAInstruction.ts @@ -0,0 +1,26 @@ +import { PublicKey, TransactionInstruction } from '@solana/web3.js' +import { createAssociatedTokenAccount } from '@utils/associated' +import { LockerData } from '../programs/lockedVoter' +import ATribecaConfiguration from '../ATribecaConfiguration' + +export async function createEscrowATAInstruction({ + lockerData, + authority, + payer, + tribecaConfiguration, +}: { + lockerData: LockerData + authority: PublicKey + payer: PublicKey + tribecaConfiguration: ATribecaConfiguration +}): Promise { + const [escrow] = await tribecaConfiguration.findEscrowAddress(authority) + + const [tx] = await createAssociatedTokenAccount( + payer, + escrow, + lockerData.tokenMint + ) + + return tx +} diff --git a/tools/sdk/tribeca/instructions/createGaugeVoteInstruction.ts b/tools/sdk/tribeca/instructions/createGaugeVoteInstruction.ts new file mode 100644 index 0000000000..d09e320e1a --- /dev/null +++ b/tools/sdk/tribeca/instructions/createGaugeVoteInstruction.ts @@ -0,0 +1,49 @@ +import { + PublicKey, + SystemProgram, + TransactionInstruction, +} from '@solana/web3.js' +import ATribecaConfiguration, { + TribecaPrograms, +} from '../ATribecaConfiguration' + +export async function createGaugeVoteInstruction({ + programs, + gauge, + authority, + payer, + tribecaConfiguration, +}: { + programs: TribecaPrograms + gauge: PublicKey + authority: PublicKey + payer: PublicKey + tribecaConfiguration: ATribecaConfiguration +}): Promise { + const [escrow] = await tribecaConfiguration.findEscrowAddress(authority) + + const [gaugeVoter] = await tribecaConfiguration.findGaugeVoterAddress(escrow) + + const [gaugeVote, bump] = await tribecaConfiguration.findGaugeVoteAddress( + gaugeVoter, + gauge + ) + + console.log('create gauge vote', { + gaugeVoter: gaugeVoter.toString(), + gaugeVote: gaugeVote.toString(), + gauge: gauge.toString(), + payer: payer.toString(), + systemProgram: SystemProgram.programId.toString(), + }) + + return programs.Gauge.instruction.createGaugeVote(bump, { + accounts: { + gaugeVoter, + gaugeVote, + gauge, + payer, + systemProgram: SystemProgram.programId, + }, + }) +} diff --git a/tools/sdk/tribeca/instructions/createGaugeVoterInstruction.ts b/tools/sdk/tribeca/instructions/createGaugeVoterInstruction.ts new file mode 100644 index 0000000000..cba54f60aa --- /dev/null +++ b/tools/sdk/tribeca/instructions/createGaugeVoterInstruction.ts @@ -0,0 +1,43 @@ +import { + PublicKey, + SystemProgram, + TransactionInstruction, +} from '@solana/web3.js' +import ATribecaConfiguration, { + TribecaPrograms, +} from '../ATribecaConfiguration' + +export async function createGaugeVoterInstruction({ + programs, + authority, + payer, + tribecaConfiguration, +}: { + programs: TribecaPrograms + authority: PublicKey + payer: PublicKey + tribecaConfiguration: ATribecaConfiguration +}): Promise { + const [escrow] = await tribecaConfiguration.findEscrowAddress(authority) + + const [gaugeVoter, bump] = await tribecaConfiguration.findGaugeVoterAddress( + escrow + ) + + console.log('Create Gauge Voter', { + escrow: escrow.toString(), + gaugeVoter: gaugeVoter.toString(), + gaugemeister: ATribecaConfiguration.gaugemeister.toString(), + payer: payer.toString(), + }) + + return programs.Gauge.instruction.createGaugeVoter(bump, { + accounts: { + gaugeVoter, + gaugemeister: ATribecaConfiguration.gaugemeister, + escrow, + payer, + systemProgram: SystemProgram.programId, + }, + }) +} diff --git a/tools/sdk/tribeca/instructions/gaugeCommitVoteInstruction.ts b/tools/sdk/tribeca/instructions/gaugeCommitVoteInstruction.ts new file mode 100644 index 0000000000..4ab759fb02 --- /dev/null +++ b/tools/sdk/tribeca/instructions/gaugeCommitVoteInstruction.ts @@ -0,0 +1,71 @@ +import { + PublicKey, + SystemProgram, + TransactionInstruction, +} from '@solana/web3.js' +import ATribecaConfiguration, { + TribecaPrograms, +} from '../ATribecaConfiguration' + +export async function gaugeCommitVoteInstruction({ + programs, + gauge, + authority, + payer, + tribecaConfiguration, +}: { + programs: TribecaPrograms + gauge: PublicKey + authority: PublicKey + payer: PublicKey + tribecaConfiguration: ATribecaConfiguration +}): Promise { + const { currentRewardsEpoch } = await tribecaConfiguration.fetchGaugemeister( + programs.Gauge + ) + + const [escrow] = await tribecaConfiguration.findEscrowAddress(authority) + + const [gaugeVoter] = await tribecaConfiguration.findGaugeVoterAddress(escrow) + + const [gaugeVote] = await tribecaConfiguration.findGaugeVoteAddress( + gaugeVoter, + gauge + ) + + const votingEpoch = currentRewardsEpoch + 1 + + const [epochGauge] = await tribecaConfiguration.findEpochGaugeAddress( + gauge, + votingEpoch + ) + + const [ + epochGaugeVoter, + ] = await tribecaConfiguration.findEpochGaugeVoterAddress( + gaugeVoter, + votingEpoch + ) + + const [ + epochGaugeVote, + voteBump, + ] = await tribecaConfiguration.findEpochGaugeVoteAddress( + gaugeVote, + votingEpoch + ) + + return programs.Gauge.instruction.gaugeCommitVote(voteBump, { + accounts: { + gaugemeister: ATribecaConfiguration.gaugemeister, + gauge, + gaugeVoter, + gaugeVote, + payer, + systemProgram: SystemProgram.programId, + epochGauge, + epochGaugeVoter, + epochGaugeVote, + }, + }) +} diff --git a/tools/sdk/tribeca/instructions/gaugeRevertVoteInstruction.ts b/tools/sdk/tribeca/instructions/gaugeRevertVoteInstruction.ts new file mode 100644 index 0000000000..6d0ff4b7ef --- /dev/null +++ b/tools/sdk/tribeca/instructions/gaugeRevertVoteInstruction.ts @@ -0,0 +1,78 @@ +import { PublicKey, TransactionInstruction } from '@solana/web3.js' +import ATribecaConfiguration, { + TribecaPrograms, +} from '../ATribecaConfiguration' + +export async function gaugeRevertVoteInstruction({ + programs, + gauge, + authority, + payer, + tribecaConfiguration, +}: { + programs: TribecaPrograms + gauge: PublicKey + authority: PublicKey + payer: PublicKey + tribecaConfiguration: ATribecaConfiguration +}): Promise { + const { currentRewardsEpoch } = await tribecaConfiguration.fetchGaugemeister( + programs.Gauge + ) + + const [escrow] = await tribecaConfiguration.findEscrowAddress(authority) + + const [gaugeVoter] = await tribecaConfiguration.findGaugeVoterAddress(escrow) + + const [gaugeVote] = await tribecaConfiguration.findGaugeVoteAddress( + gaugeVoter, + gauge + ) + + const votingEpoch = currentRewardsEpoch + 1 + + const [epochGauge] = await tribecaConfiguration.findEpochGaugeAddress( + gauge, + votingEpoch + ) + + const [ + epochGaugeVoter, + ] = await tribecaConfiguration.findEpochGaugeVoterAddress( + gaugeVoter, + votingEpoch + ) + + const [epochGaugeVote] = await tribecaConfiguration.findEpochGaugeVoteAddress( + gaugeVote, + votingEpoch + ) + + console.log('Gauge Revert Vote', { + gaugemeister: ATribecaConfiguration.gaugemeister.toString(), + gauge: gauge.toString(), + gaugeVoter: gaugeVoter.toString(), + gaugeVote: gaugeVote.toString(), + epochGauge: epochGauge.toString(), + epochGaugeVoter: epochGaugeVoter.toString(), + escrow: escrow.toString(), + voteDelegate: authority.toString(), + epochGaugeVote: epochGaugeVote.toString(), + payer: payer.toString(), + }) + + return programs.Gauge.instruction.gaugeRevertVote({ + accounts: { + gaugemeister: ATribecaConfiguration.gaugemeister, + gauge, + gaugeVoter, + gaugeVote, + epochGauge, + epochGaugeVoter, + escrow, + voteDelegate: authority, + epochGaugeVote, + payer, + }, + }) +} diff --git a/tools/sdk/tribeca/instructions/gaugeSetVoteInstruction.ts b/tools/sdk/tribeca/instructions/gaugeSetVoteInstruction.ts new file mode 100644 index 0000000000..04fcb7e9c1 --- /dev/null +++ b/tools/sdk/tribeca/instructions/gaugeSetVoteInstruction.ts @@ -0,0 +1,46 @@ +import { PublicKey, TransactionInstruction } from '@solana/web3.js' +import ATribecaConfiguration, { + TribecaPrograms, +} from '../ATribecaConfiguration' + +export async function gaugeSetVoteInstruction({ + weight, + programs, + gauge, + authority, + tribecaConfiguration, +}: { + weight: number + programs: TribecaPrograms + gauge: PublicKey + authority: PublicKey + tribecaConfiguration: ATribecaConfiguration +}): Promise { + const [escrow] = await tribecaConfiguration.findEscrowAddress(authority) + + const [gaugeVoter] = await tribecaConfiguration.findGaugeVoterAddress(escrow) + + const [gaugeVote] = await tribecaConfiguration.findGaugeVoteAddress( + gaugeVoter, + gauge + ) + + console.log('Gauge Set Vote', { + escrow: escrow.toString(), + gauge: gauge.toString(), + gaugeVoter: gaugeVoter.toString(), + gaugeVote: gaugeVote.toString(), + gaugemeister: ATribecaConfiguration.gaugemeister.toString(), + }) + + return programs.Gauge.instruction.gaugeSetVote(weight, { + accounts: { + escrow, + gaugemeister: ATribecaConfiguration.gaugemeister, + gauge, + gaugeVoter, + gaugeVote, + voteDelegate: authority, + }, + }) +} diff --git a/tools/sdk/tribeca/instructions/lockInstruction.ts b/tools/sdk/tribeca/instructions/lockInstruction.ts new file mode 100644 index 0000000000..c3ce667e30 --- /dev/null +++ b/tools/sdk/tribeca/instructions/lockInstruction.ts @@ -0,0 +1,83 @@ +import { BN } from '@project-serum/anchor' +import { TOKEN_PROGRAM_ID } from '@solana/spl-token' +import { + PublicKey, + SYSVAR_INSTRUCTIONS_PUBKEY, + TransactionInstruction, +} from '@solana/web3.js' +import { getATAAddress } from '@saberhq/token-utils' +import { LockerData } from '../programs' +import ATribecaConfiguration, { + TribecaPrograms, +} from '../ATribecaConfiguration' +import { DEFAULT_GOVERNANCE_PROGRAM_ID } from '@components/instructions/tools' + +export async function lockInstruction({ + programs, + lockerData, + authority, + amount, + durationSeconds, + tribecaConfiguration, +}: { + programs: TribecaPrograms + lockerData: LockerData + authority: PublicKey + amount: BN + durationSeconds: BN + tribecaConfiguration: ATribecaConfiguration +}): Promise { + const [escrow] = await tribecaConfiguration.findEscrowAddress(authority) + + const { + tokens: escrowTokens, + owner: escrowOwner, + } = await programs.LockedVoter.account.escrow.fetch(escrow) + + const sourceTokens = await getATAAddress({ + mint: tribecaConfiguration.token.mint, + owner: escrowOwner, + }) + + const [whitelistEntry] = await tribecaConfiguration.findWhitelistAddress( + tribecaConfiguration.locker, + new PublicKey(DEFAULT_GOVERNANCE_PROGRAM_ID), + authority + ) + + console.log({ + locker: tribecaConfiguration.locker.toString(), + escrow: escrow.toString(), + escrowOwner: escrowOwner.toString(), + escrowTokens: escrowTokens.toString(), + sourceTokens: sourceTokens.toString(), + tokenProgram: TOKEN_PROGRAM_ID.toString(), + whitelistEntry: whitelistEntry.toString(), + SYSVAR_INSTRUCTIONS_PUBKEY: SYSVAR_INSTRUCTIONS_PUBKEY.toString(), + }) + + return programs.LockedVoter.instruction.lock(amount, durationSeconds, { + accounts: { + locker: tribecaConfiguration.locker, + escrow, + escrowOwner, + escrowTokens, + sourceTokens, + tokenProgram: TOKEN_PROGRAM_ID, + }, + remainingAccounts: lockerData.params.whitelistEnabled + ? [ + { + pubkey: SYSVAR_INSTRUCTIONS_PUBKEY, + isSigner: false, + isWritable: false, + }, + { + pubkey: whitelistEntry, + isSigner: false, + isWritable: false, + }, + ] + : [], + }) +} diff --git a/tools/sdk/tribeca/instructions/newEscrowInstruction.ts b/tools/sdk/tribeca/instructions/newEscrowInstruction.ts new file mode 100644 index 0000000000..e47e0d06d9 --- /dev/null +++ b/tools/sdk/tribeca/instructions/newEscrowInstruction.ts @@ -0,0 +1,32 @@ +import { + PublicKey, + SystemProgram, + TransactionInstruction, +} from '@solana/web3.js' +import ATribecaConfiguration, { + TribecaPrograms, +} from '../ATribecaConfiguration' + +export async function newEscrowInstruction({ + programs, + authority, + payer, + tribecaConfiguration, +}: { + programs: TribecaPrograms + authority: PublicKey + payer: PublicKey + tribecaConfiguration: ATribecaConfiguration +}): Promise { + const [escrow, bump] = await tribecaConfiguration.findEscrowAddress(authority) + + return programs.LockedVoter.instruction.newEscrow(bump, { + accounts: { + escrow, + payer, + locker: tribecaConfiguration.locker, + escrowOwner: authority, + systemProgram: SystemProgram.programId, + }, + }) +} diff --git a/tools/sdk/tribeca/instructions/prepareEpochGaugeVoterInstruction.ts b/tools/sdk/tribeca/instructions/prepareEpochGaugeVoterInstruction.ts new file mode 100644 index 0000000000..2b6ede66aa --- /dev/null +++ b/tools/sdk/tribeca/instructions/prepareEpochGaugeVoterInstruction.ts @@ -0,0 +1,58 @@ +import { + PublicKey, + SystemProgram, + TransactionInstruction, +} from '@solana/web3.js' +import ATribecaConfiguration, { + TribecaPrograms, +} from '../ATribecaConfiguration' + +export async function prepareEpochGaugeVoterInstruction({ + programs, + authority, + payer, + tribecaConfiguration, +}: { + programs: TribecaPrograms + authority: PublicKey + payer: PublicKey + tribecaConfiguration: ATribecaConfiguration +}): Promise { + const { + currentRewardsEpoch, + locker, + } = await tribecaConfiguration.fetchGaugemeister(programs.Gauge) + + const [escrow] = await tribecaConfiguration.findEscrowAddress(authority) + + const [gaugeVoter] = await tribecaConfiguration.findGaugeVoterAddress(escrow) + + const [ + epochGaugeVoter, + bump, + ] = await tribecaConfiguration.findEpochGaugeVoterAddress( + gaugeVoter, + currentRewardsEpoch + 1 + ) + + console.log('Prepare Epoch Gauge', { + currentRewardsEpoch, + nextEpoch: currentRewardsEpoch + 1, + escrow: escrow.toString(), + gaugeVoter: gaugeVoter.toString(), + epochGaugeVoter: epochGaugeVoter.toString(), + gaugemeister: ATribecaConfiguration.gaugemeister.toString(), + }) + + return programs.Gauge.instruction.prepareEpochGaugeVoter(bump, { + accounts: { + gaugemeister: ATribecaConfiguration.gaugemeister, + locker, + escrow, + gaugeVoter, + payer, + systemProgram: SystemProgram.programId, + epochGaugeVoter, + }, + }) +} diff --git a/tools/sdk/tribeca/instructions/resetEpochGaugeVoterInstruction.ts b/tools/sdk/tribeca/instructions/resetEpochGaugeVoterInstruction.ts new file mode 100644 index 0000000000..6fc71cd44a --- /dev/null +++ b/tools/sdk/tribeca/instructions/resetEpochGaugeVoterInstruction.ts @@ -0,0 +1,49 @@ +import { PublicKey, TransactionInstruction } from '@solana/web3.js' +import ATribecaConfiguration, { + TribecaPrograms, +} from '../ATribecaConfiguration' + +export async function resetEpochGaugeVoterInstruction({ + programs, + authority, + tribecaConfiguration, +}: { + programs: TribecaPrograms + authority: PublicKey + tribecaConfiguration: ATribecaConfiguration +}): Promise { + const { currentRewardsEpoch } = await tribecaConfiguration.fetchGaugemeister( + programs.Gauge + ) + + const [escrow] = await tribecaConfiguration.findEscrowAddress(authority) + + const [gaugeVoter] = await tribecaConfiguration.findGaugeVoterAddress(escrow) + + const votingEpoch = currentRewardsEpoch + 1 + + const [ + epochGaugeVoter, + ] = await tribecaConfiguration.findEpochGaugeVoterAddress( + gaugeVoter, + votingEpoch + ) + + console.log('Reset Epoch Gauge Voter', { + gaugemeister: ATribecaConfiguration.gaugemeister.toString(), + locker: tribecaConfiguration.locker.toString(), + escrow: escrow.toString(), + gaugeVoter: gaugeVoter.toString(), + epochGaugeVoter: epochGaugeVoter.toString(), + }) + + return programs.Gauge.instruction.resetEpochGaugeVoter({ + accounts: { + gaugemeister: ATribecaConfiguration.gaugemeister, + locker: tribecaConfiguration.locker, + escrow, + gaugeVoter, + epochGaugeVoter, + }, + }) +} diff --git a/utils/uiTypes/proposalCreationTypes.ts b/utils/uiTypes/proposalCreationTypes.ts index f140a4336b..8a0ebf02cc 100644 --- a/utils/uiTypes/proposalCreationTypes.ts +++ b/utils/uiTypes/proposalCreationTypes.ts @@ -14,6 +14,7 @@ import { SplTokenUIName } from '@utils/splTokens' import { DepositWithMintAccount, Voter } from 'VoteStakeRegistry/sdk/accounts' import { LockupKind } from 'VoteStakeRegistry/tools/types' import { AmountSide } from '@raydium-io/raydium-sdk' +import ATribecaConfiguration from '@tools/sdk/tribeca/ATribecaConfiguration' export interface UiInstruction { serializedInstruction: string @@ -186,6 +187,64 @@ export interface WithdrawInsuranceFromMangoDepositoryForm { insuranceWithdrawnAmount: number } +export interface TribecaCreateEpochGaugeForm { + governedAccount?: GovernedMultiTypeAccount + gaugeName?: string +} + +export interface TribecaCreateEscrowGovernanceTokenATAForm { + governedAccount?: GovernedMultiTypeAccount + tribecaConfiguration: ATribecaConfiguration | null +} + +export interface TribecaCreateGaugeVoteForm { + governedAccount?: GovernedMultiTypeAccount + gaugeName?: string +} + +export interface TribecaCreateGaugeVoterForm { + governedAccount?: GovernedMultiTypeAccount + tribecaConfiguration: ATribecaConfiguration | null +} + +export interface TribecaGaugeCommitVoteForm { + governedAccount?: GovernedMultiTypeAccount + gaugeName?: string +} + +export interface TribecaGaugeRevertVoteForm { + governedAccount?: GovernedMultiTypeAccount + gaugeName?: string +} + +export interface TribecaLockForm { + governedAccount?: GovernedMultiTypeAccount + tribecaConfiguration: ATribecaConfiguration | null + uiAmount: number + durationSeconds: number +} + +export interface TribecaNewEscrowForm { + governedAccount?: GovernedMultiTypeAccount + tribecaConfiguration: ATribecaConfiguration | null +} + +export interface TribecaPrepareEpochGaugeVoterForm { + governedAccount?: GovernedMultiTypeAccount + tribecaConfiguration: ATribecaConfiguration | null +} + +export interface TribecaResetEpochGaugeVoterForm { + governedAccount?: GovernedMultiTypeAccount + tribecaConfiguration: ATribecaConfiguration | null +} + +export interface TribecaGaugeSetVoteForm { + governedAccount?: GovernedMultiTypeAccount + gaugeName?: string + weight?: number +} + export enum Instructions { Transfer, ProgramUpgrade, @@ -205,6 +264,17 @@ export enum Instructions { SolendWithdrawObligationCollateralAndRedeemReserveLiquidity, SolendRefreshObligation, SolendRefreshReserve, + TribecaCreateEpochGauge, + TribecaCreateEscrowGovernanceTokenATA, + TribecaCreateGaugeVote, + TribecaCreateGaugeVoter, + TribecaGaugeCommitVote, + TribecaGaugeRevertVote, + TribecaLock, + TribecaNewEscrow, + TribecaPrepareEpochGaugeVoter, + TribecaResetEpochGaugeVoter, + TribecaGaugeSetVote, UXDInitializeController, UXDSetRedeemableGlobalSupplyCap, UXDSetMangoDepositoriesRedeemableSoftCap, From 0ac1d38943a83bd9e8f294ac560af89cc04a6a2d Mon Sep 17 00:00:00 2001 From: GregoryNEUT Date: Thu, 7 Apr 2022 09:37:47 +0200 Subject: [PATCH 121/204] Rework proposal new component (#52) * Split new proposal component * Start to allow user to select Package for instructions * add pictures and filter instruction by name * Move new components to Component/ instead of pages/ * Deeper refacto of the new component + childrens * type new Form * handle proper package separation * migrate native components + note Mint/Transfer as deprecated * move PackageSelection to Components/ * instruction naming * handle newly implemented tribeca instructions --- .../components/instructions/Clawback.tsx | 10 +- .../components/instructions/Grant.tsx | 13 +- actions/createProposal.ts | 4 +- components/AssetsList/CloseBuffers.tsx | 10 +- components/AssetsList/UpgradeProgram.tsx | 8 +- components/InstructionForm.tsx | 69 +++ components/InstructionsForm.tsx | 93 ++++ components/Members/AddMemberForm.tsx | 9 +- components/PackageSelection.tsx | 55 ++ components/SelectInstructionType.tsx | 141 ++++++ components/TreasuryAccount/ConvertToMsol.tsx | 4 +- components/TreasuryAccount/SendTokens.tsx | 6 +- components/inputs/Input.tsx | 1 + components/inputs/Select.tsx | 6 +- .../ExecuteAllInstructionButton.tsx | 5 +- .../instructions/ExecuteInstructionButton.tsx | 5 +- hooks/useGovernanceAssets.ts | 288 +++++++---- hooks/useHotWalletPluginTokenAccounts.ts | 2 +- hooks/useInstructionFormBuilder.ts | 49 +- .../components/DryRunInstructionBtn.tsx | 4 +- .../components/{instructions => }/Execute.tsx | 0 .../InstructionContentContainer.tsx | 18 +- .../proposal/components/VoteBySwitch.tsx | 6 +- .../components/instructions/CustomBase64.tsx | 147 ------ .../instructions/Friktion/FriktionDeposit.tsx | 8 +- .../CreateAssociatedTokenAccount.tsx | 0 .../instructions/Native/CustomBase64.tsx | 113 +++++ .../instructions/{ => Native}/Empty.tsx | 2 +- .../instructions/{ => Native}/Mint.tsx | 20 +- .../{ => Native}/SetProgramAuthority.tsx | 3 +- .../{ => Native}/SplTokenTransfer.tsx | 15 +- .../components/instructions/ProposalForm.tsx | 56 -- .../instructions/SelectedInstruction.tsx | 111 ++-- .../bpfUpgradeableLoader/ProgramUpgrade.tsx | 2 +- pages/dao/[symbol]/proposal/new.tsx | 477 +++++++----------- public/img/friktion.png | Bin 0 -> 126351 bytes public/img/raydium.png | Bin 0 -> 15347 bytes public/img/solend.png | Bin 0 -> 368384 bytes public/img/tribeca.png | Bin 0 -> 6037 bytes public/img/uxd.png | Bin 0 -> 6047 bytes public/img/vote-stake-registry.png | Bin 0 -> 63590 bytes stores/useWalletStore.tsx | 4 +- utils/instructionTools.ts | 26 +- utils/tokens.tsx | 10 +- utils/uiTypes/proposalCreationTypes.ts | 46 +- yarn.lock | 20 +- 46 files changed, 1087 insertions(+), 779 deletions(-) create mode 100644 components/InstructionForm.tsx create mode 100644 components/InstructionsForm.tsx create mode 100644 components/PackageSelection.tsx create mode 100644 components/SelectInstructionType.tsx rename pages/dao/[symbol]/proposal/components/{instructions => }/Execute.tsx (100%) delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx rename pages/dao/[symbol]/proposal/components/instructions/{ => Native}/CreateAssociatedTokenAccount.tsx (100%) create mode 100644 pages/dao/[symbol]/proposal/components/instructions/Native/CustomBase64.tsx rename pages/dao/[symbol]/proposal/components/instructions/{ => Native}/Empty.tsx (91%) rename pages/dao/[symbol]/proposal/components/instructions/{ => Native}/Mint.tsx (94%) rename pages/dao/[symbol]/proposal/components/instructions/{ => Native}/SetProgramAuthority.tsx (97%) rename pages/dao/[symbol]/proposal/components/instructions/{ => Native}/SplTokenTransfer.tsx (95%) delete mode 100644 pages/dao/[symbol]/proposal/components/instructions/ProposalForm.tsx create mode 100644 public/img/friktion.png create mode 100644 public/img/raydium.png create mode 100644 public/img/solend.png create mode 100644 public/img/tribeca.png create mode 100644 public/img/uxd.png create mode 100644 public/img/vote-stake-registry.png diff --git a/VoteStakeRegistry/components/instructions/Clawback.tsx b/VoteStakeRegistry/components/instructions/Clawback.tsx index 799afeb537..22786ed7aa 100644 --- a/VoteStakeRegistry/components/instructions/Clawback.tsx +++ b/VoteStakeRegistry/components/instructions/Clawback.tsx @@ -5,7 +5,7 @@ import useWalletStore from 'stores/useWalletStore' import { GovernedMultiTypeAccount, tryGetMint } from '@utils/tokens' import { ClawbackForm, - UiInstruction, + FormInstructionData, } from '@utils/uiTypes/proposalCreationTypes' import useGovernanceAssets from '@hooks/useGovernanceAssets' import { @@ -58,12 +58,12 @@ const Clawback = ({ ProgramAccount | undefined >(undefined) const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) + const { handleSetInstruction } = useContext(NewProposalContext) const handleSetForm = ({ propertyName, value }) => { setFormErrors({}) setForm({ ...form, [propertyName]: value }) } - async function getInstruction(): Promise { + async function getInstruction(): Promise { const isValid = await validateInstruction({ schema, form, setFormErrors }) let serializedInstruction = '' const prerequisiteInstructions: TransactionInstruction[] = [] @@ -91,7 +91,7 @@ const Clawback = ({ serializedInstruction = serializeInstructionToBase64(clawbackIx!) } - const obj: UiInstruction = { + const obj: FormInstructionData = { serializedInstruction, isValid, governance: governancesArray.find( @@ -103,7 +103,7 @@ const Clawback = ({ return obj } useEffect(() => { - handleSetInstructions( + handleSetInstruction( { governedAccount: governedAccount, getInstruction }, index ) diff --git a/VoteStakeRegistry/components/instructions/Grant.tsx b/VoteStakeRegistry/components/instructions/Grant.tsx index c6f840b053..52f9e36a73 100644 --- a/VoteStakeRegistry/components/instructions/Grant.tsx +++ b/VoteStakeRegistry/components/instructions/Grant.tsx @@ -15,7 +15,10 @@ import { TokenProgramAccount, tryGetTokenAccount, } from '@utils/tokens' -import { GrantForm, UiInstruction } from '@utils/uiTypes/proposalCreationTypes' +import { + GrantForm, + FormInstructionData, +} from '@utils/uiTypes/proposalCreationTypes' import { getAccountName } from '@components/instructions/tools' import { debounce } from '@utils/debounce' import { getTokenTransferSchema } from '@utils/validations' @@ -80,7 +83,7 @@ const Grant = ({ ? getMintMinAmountAsDecimal(form.mintInfo) : 1 const currentPrecision = precision(mintMinAmount) - const { handleSetInstructions } = useContext(NewProposalContext) + const { handleSetInstruction } = useContext(NewProposalContext) const handleSetForm = ({ propertyName, value }) => { setFormErrors({}) setForm({ ...form, [propertyName]: value }) @@ -108,7 +111,7 @@ const Grant = ({ propertyName: 'amount', }) } - async function getInstruction(): Promise { + async function getInstruction(): Promise { const isValid = await validateInstruction({ schema, form, setFormErrors }) let serializedInstruction = '' const prerequisiteInstructions: TransactionInstruction[] = [] @@ -153,7 +156,7 @@ const Grant = ({ serializedInstruction = serializeInstructionToBase64(grantIx!) } - const obj: UiInstruction = { + const obj: FormInstructionData = { serializedInstruction, isValid, governance: form.governedTokenAccount?.governance, @@ -209,7 +212,7 @@ const Grant = ({ } }, [form.destinationAccount]) useEffect(() => { - handleSetInstructions( + handleSetInstruction( { governedAccount: governedAccount, getInstruction }, index ) diff --git a/actions/createProposal.ts b/actions/createProposal.ts index 543dd01754..1189184bf6 100644 --- a/actions/createProposal.ts +++ b/actions/createProposal.ts @@ -25,7 +25,7 @@ import { withUpdateVoterWeightRecord } from 'VoteStakeRegistry/sdk/withUpdateVot import { VsrClient } from '@blockworks-foundation/voter-stake-registry-client' import { sendTransactions, SequenceType } from '@utils/sendTransactions' import { chunks } from '@utils/helpers' -import { UiInstruction } from '@utils/uiTypes/proposalCreationTypes' +import { FormInstructionData } from '@utils/uiTypes/proposalCreationTypes' export interface InstructionDataWithHoldUpTime { data: InstructionData | null @@ -41,7 +41,7 @@ export class InstructionDataWithHoldUpTime { instruction, governance, }: { - instruction: UiInstruction + instruction: FormInstructionData governance?: ProgramAccount }) { this.data = instruction.serializedInstruction diff --git a/components/AssetsList/CloseBuffers.tsx b/components/AssetsList/CloseBuffers.tsx index 2e5b08fcf5..f576d3d027 100644 --- a/components/AssetsList/CloseBuffers.tsx +++ b/components/AssetsList/CloseBuffers.tsx @@ -9,7 +9,7 @@ import VoteBySwitch from 'pages/dao/[symbol]/proposal/components/VoteBySwitch' import useWalletStore from 'stores/useWalletStore' import { getValidatedPublickKey } from 'utils/validations' import { useEffect, useState } from 'react' -import { UiInstruction } from 'utils/uiTypes/proposalCreationTypes' +import { FormInstructionData } from 'utils/uiTypes/proposalCreationTypes' import { serializeInstructionToBase64 } from '@solana/spl-governance' import { useRouter } from 'next/router' import { notify } from 'utils/notifications' @@ -117,9 +117,9 @@ const CloseBuffers = ({ program }: { program: ProgramAccount }) => { .nullable() .required('Program governed account is required'), }) - async function getInstructions(): Promise { + async function getInstructions(): Promise { const isValid = await validateInstruction({ schema, form, setFormErrors }) - const instructions: UiInstruction[] = [] + const instructions: FormInstructionData[] = [] for (let i = 0; i < buffers.length; i++) { let serializedInstruction = '' if ( @@ -135,7 +135,7 @@ const CloseBuffers = ({ program }: { program: ProgramAccount }) => { ) serializedInstruction = serializeInstructionToBase64(closeIx) } - const obj: UiInstruction = { + const obj: FormInstructionData = { serializedInstruction: serializedInstruction, isValid, governance: form.governedAccount?.governance, @@ -146,7 +146,7 @@ const CloseBuffers = ({ program }: { program: ProgramAccount }) => { } const handlePropose = async () => { setIsLoading(true) - const instructions: UiInstruction[] = await getInstructions() + const instructions: FormInstructionData[] = await getInstructions() if (instructions.length && instructions[0].isValid) { const governance = form.governedAccount?.governance if (!realm) { diff --git a/components/AssetsList/UpgradeProgram.tsx b/components/AssetsList/UpgradeProgram.tsx index 006459ed6d..8a0ee691f9 100644 --- a/components/AssetsList/UpgradeProgram.tsx +++ b/components/AssetsList/UpgradeProgram.tsx @@ -10,7 +10,7 @@ import { validateBuffer } from 'utils/validations' import { useEffect, useState } from 'react' import { ProgramUpgradeForm, - UiInstruction, + FormInstructionData, } from 'utils/uiTypes/proposalCreationTypes' import { getInstructionDataFromBase64, @@ -106,7 +106,7 @@ const UpgradeProgram = ({ .nullable() .required('Program governed account is required'), }) - async function getInstruction(): Promise { + async function getInstruction(): Promise { const isValid = await validateInstruction({ schema, form, setFormErrors }) let serializedInstruction = '' if ( @@ -124,7 +124,7 @@ const UpgradeProgram = ({ ) serializedInstruction = serializeInstructionToBase64(upgradeIx) } - const obj: UiInstruction = { + const obj: FormInstructionData = { serializedInstruction: serializedInstruction, isValid, governance: form.governedAccount?.governance, @@ -133,7 +133,7 @@ const UpgradeProgram = ({ } const handlePropose = async () => { setIsLoading(true) - const instruction: UiInstruction = await getInstruction() + const instruction: FormInstructionData = await getInstruction() if (instruction.isValid) { const governance = form.governedAccount?.governance let proposalAddress: PublicKey | null = null diff --git a/components/InstructionForm.tsx b/components/InstructionForm.tsx new file mode 100644 index 0000000000..33fb565470 --- /dev/null +++ b/components/InstructionForm.tsx @@ -0,0 +1,69 @@ +import { LinkButton } from '@components/Button' +import { XCircleIcon } from '@heroicons/react/solid' +import { InstructionType } from '@hooks/useGovernanceAssets' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { ComponentInstructionData } from '@utils/uiTypes/proposalCreationTypes' +import SelectedInstruction from 'pages/dao/[symbol]/proposal/components/instructions/SelectedInstruction' +import InstructionContentContainer from '../pages/dao/[symbol]/proposal/components/InstructionContentContainer' +import SelectInstructionType from './SelectInstructionType' + +const InstructionForm = ({ + idx, + availableInstructions, + governedAccount, + selectedInstruction, + setInstructionType, + removeInstruction, +}: { + idx: number + selectedInstruction: ComponentInstructionData + governedAccount?: GovernedMultiTypeAccount + availableInstructions: InstructionType[] + + setInstructionType: ({ + instructionType, + idx, + }: { + instructionType: InstructionType | null + idx: number + }) => void + + removeInstruction: (idx: number) => void +}) => { + return ( +
+ + setInstructionType({ instructionType, idx }) + } + selectedInstruction={selectedInstruction.type} + /> + +
+ + {selectedInstruction.type ? ( + + ) : null} + + + {idx != 0 ? ( + removeInstruction(idx)} + > + + Remove + + ) : null} +
+
+ ) +} + +export default InstructionForm diff --git a/components/InstructionsForm.tsx b/components/InstructionsForm.tsx new file mode 100644 index 0000000000..f15e7d8804 --- /dev/null +++ b/components/InstructionsForm.tsx @@ -0,0 +1,93 @@ +import { LinkButton } from '@components/Button' +import { PlusCircleIcon } from '@heroicons/react/solid' +import { InstructionType } from '@hooks/useGovernanceAssets' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import { ComponentInstructionData } from '@utils/uiTypes/proposalCreationTypes' +import { NewProposalContext } from 'pages/dao/[symbol]/proposal/new' +import { useState } from 'react' +import InstructionForm from './InstructionForm' + +const InstructionsForm = ({ + availableInstructions, + onInstructionsChange, + governedAccount, +}: { + availableInstructions: InstructionType[] + onInstructionsChange: (instructions: ComponentInstructionData[]) => void + governedAccount?: GovernedMultiTypeAccount +}) => { + const [instructions, setInstructions] = useState([ + // One empty instruction at start + {}, + ]) + + const handleSetInstruction = ( + val: Partial, + idx: number + ) => { + const newInstructions = [...instructions] + newInstructions[idx] = { ...instructions[idx], ...val } + setInstructions(newInstructions) + onInstructionsChange(newInstructions) + } + + const setInstructionType = ({ + instructionType, + idx, + }: { + instructionType: InstructionType | null + idx: number + }) => { + handleSetInstruction( + { + type: instructionType ? instructionType : undefined, + }, + idx + ) + } + + const addInstruction = () => { + setInstructions([...instructions, { type: undefined }]) + } + + const removeInstruction = (idx: number) => { + setInstructions([...instructions.filter((x, index) => index !== idx)]) + } + + return ( + <> + +

Instructions

+ + {instructions.map((instruction, idx) => ( + + ))} +
+ +
+ + + Add instruction + +
+ + ) +} + +export default InstructionsForm diff --git a/components/Members/AddMemberForm.tsx b/components/Members/AddMemberForm.tsx index e07ec3fb2e..97205097d9 100644 --- a/components/Members/AddMemberForm.tsx +++ b/components/Members/AddMemberForm.tsx @@ -8,7 +8,10 @@ import { abbreviateAddress, precision } from 'utils/formatting' import useWalletStore from 'stores/useWalletStore' import { getMintSchema } from 'utils/validations' import { useEffect, useState } from 'react' -import { MintForm, UiInstruction } from 'utils/uiTypes/proposalCreationTypes' +import { + MintForm, + FormInstructionData, +} from 'utils/uiTypes/proposalCreationTypes' import useGovernanceAssets from 'hooks/useGovernanceAssets' import { getInstructionDataFromBase64, @@ -112,7 +115,7 @@ const AddMemberForm = ({ close }) => { }) } - const getInstruction = async (): Promise => { + const getInstruction = async (): Promise => { return getMintInstruction({ schema, form, @@ -128,7 +131,7 @@ const AddMemberForm = ({ close }) => { const handlePropose = async () => { setIsLoading(true) - const instruction: UiInstruction = await getInstruction() + const instruction: FormInstructionData = await getInstruction() if (instruction.isValid && wallet && realmInfo) { const governance = form.mintAccount?.governance diff --git a/components/PackageSelection.tsx b/components/PackageSelection.tsx new file mode 100644 index 0000000000..bb3dee15cf --- /dev/null +++ b/components/PackageSelection.tsx @@ -0,0 +1,55 @@ +import { PackageType } from '@hooks/useGovernanceAssets' +import { PackageEnum } from '@utils/uiTypes/proposalCreationTypes' + +const PackageSelection = ({ + selected, + className, + onClick, + packages, +}: { + selected: PackageEnum | null + className?: string | undefined + onClick: (selected: PackageEnum) => void + packages: PackageType[] +}) => { + return ( +
+ {packages.map(({ id, name, image }) => { + if (image) { + return ( + onClick(Number(id) as PackageEnum)} + /> + ) + } + + // There is no image, we use the text instead + return ( + onClick(Number(id) as PackageEnum)} + key={name} + > + {name} + + ) + })} +
+ ) +} + +export default PackageSelection diff --git a/components/SelectInstructionType.tsx b/components/SelectInstructionType.tsx new file mode 100644 index 0000000000..fc714485b8 --- /dev/null +++ b/components/SelectInstructionType.tsx @@ -0,0 +1,141 @@ +import Select from '@components/inputs/Select' +import useGovernanceAssets, { + InstructionType, +} from '@hooks/useGovernanceAssets' +import { PackageEnum } from '@utils/uiTypes/proposalCreationTypes' +import { useCallback, useEffect, useState } from 'react' +import PackageSelection from './PackageSelection' + +const SelectInstructionType = ({ + idx, + instructionTypes, + selectedInstruction, + onChange, +}: { + idx: number + selectedInstruction?: InstructionType + instructionTypes: InstructionType[] + + onChange: ({ + instructionType, + idx, + }: { + instructionType: InstructionType | null + idx: number + }) => void +}) => { + const [packageId, setPackageId] = useState(null) + const { availablePackages, getPackageTypeById } = useGovernanceAssets() + + const [filteredInstructionTypes, setFilteredInstructionTypes] = useState< + InstructionType[] + >([]) + + const computeFilteredInstructionsTypes = useCallback(() => { + if (packageId === null) { + setFilteredInstructionTypes(instructionTypes) + + // Select first instruction by default + if (instructionTypes.length && !selectedInstruction) { + onChange({ instructionType: instructionTypes[0], idx }) + } + + return + } + + if (selectedInstruction && selectedInstruction.packageId !== packageId) { + onChange({ instructionType: null, idx }) + } + + const filteredInstructionTypes = instructionTypes.filter( + (instructionType) => instructionType.packageId === packageId + ) + + // Select first instruction by default + if (filteredInstructionTypes.length && !selectedInstruction) { + onChange({ instructionType: filteredInstructionTypes[0], idx }) + } + + setFilteredInstructionTypes(filteredInstructionTypes) + }, [packageId, instructionTypes]) + + useEffect(() => { + computeFilteredInstructionsTypes() + }, [computeFilteredInstructionsTypes]) + + // Only display the package name is a no package is selected + const getInstructionDisplayName = ( + instruction?: InstructionType + ): string | JSX.Element => { + if (!instruction) { + return '' + } + + return ( + <> + {packageId === null ? ( + + {getPackageTypeById(instruction.packageId)!.name}: + + ) : null} + + {instruction.name} + + {instruction.tag ? ( + + {instruction.tag} + + ) : null} + + ) + } + + return ( +
+
+ Instruction {idx + 1} + + + Filters by package + + + { + // Clicking on selected packageName unselect it + if (selectedPackageId === packageId) { + setPackageId(null) + return + } + + setPackageId(selectedPackageId) + }} + /> +
+ + +
+ ) +} + +export default SelectInstructionType diff --git a/components/TreasuryAccount/ConvertToMsol.tsx b/components/TreasuryAccount/ConvertToMsol.tsx index 2811a55a87..d8231b62bf 100644 --- a/components/TreasuryAccount/ConvertToMsol.tsx +++ b/components/TreasuryAccount/ConvertToMsol.tsx @@ -7,7 +7,7 @@ import { GovernedMultiTypeAccount } from '@utils/tokens' import { useEffect, useState } from 'react' import { StakingViewForm, - UiInstruction, + FormInstructionData, } from '@utils/uiTypes/proposalCreationTypes' import { getMintMinAmountAsDecimal } from '@tools/sdk/units' import Input from '@components/inputs/Input' @@ -86,7 +86,7 @@ const ConvertToMsol = () => { const handlePropose = async () => { setIsLoading(true) - const instruction: UiInstruction = await getConvertToMsolInstruction({ + const instruction: FormInstructionData = await getConvertToMsolInstruction({ schema, form, connection, diff --git a/components/TreasuryAccount/SendTokens.tsx b/components/TreasuryAccount/SendTokens.tsx index d2e0d6146a..9a44aa0ff8 100644 --- a/components/TreasuryAccount/SendTokens.tsx +++ b/components/TreasuryAccount/SendTokens.tsx @@ -15,7 +15,7 @@ import { abbreviateAddress, precision } from '@utils/formatting' import { TokenProgramAccount, tryGetTokenAccount } from '@utils/tokens' import { SendTokenCompactViewForm, - UiInstruction, + FormInstructionData, } from '@utils/uiTypes/proposalCreationTypes' import React, { useEffect, useState } from 'react' import useTreasuryAccountStore from 'stores/useTreasuryAccountStore' @@ -148,7 +148,7 @@ const SendTokens = () => { return totalPriceFormatted } - async function getInstruction(): Promise { + async function getInstruction(): Promise { const selectedNftMint = selectedNfts[0]?.mint const defaultProps = { schema, @@ -173,7 +173,7 @@ const SendTokens = () => { } const handlePropose = async () => { setIsLoading(true) - const instruction: UiInstruction = await getInstruction() + const instruction: FormInstructionData = await getInstruction() if (instruction.isValid) { const governance = currentAccount?.governance let proposalAddress: PublicKey | null = null diff --git a/components/inputs/Input.tsx b/components/inputs/Input.tsx index 2aafd82fcb..34c2da5e7c 100644 --- a/components/inputs/Input.tsx +++ b/components/inputs/Input.tsx @@ -37,6 +37,7 @@ const Input = ({ return (
{label && {label}} + {subtitle &&

{subtitle}

} {prefix ? (
-
+
{componentLabel ? componentLabel : value ? value : placeholder} diff --git a/components/instructions/ExecuteAllInstructionButton.tsx b/components/instructions/ExecuteAllInstructionButton.tsx index de9b588955..bd1a48ba22 100644 --- a/components/instructions/ExecuteAllInstructionButton.tsx +++ b/components/instructions/ExecuteAllInstructionButton.tsx @@ -77,7 +77,10 @@ export function ExecuteAllInstructionButton({ await executeInstructions(rpcContext, proposal, proposalInstructions) await fetchRealm(realmInfo?.programId, realmInfo?.realmId) } catch (error) { - notify({ type: 'error', message: `error executing instruction ${error}` }) + notify({ + type: 'error', + message: `error executing instruction ${error}`, + }) console.error('error executing instruction', error) setPlaying(PlayState.Error) diff --git a/components/instructions/ExecuteInstructionButton.tsx b/components/instructions/ExecuteInstructionButton.tsx index af831e98bd..e72268c40d 100644 --- a/components/instructions/ExecuteInstructionButton.tsx +++ b/components/instructions/ExecuteInstructionButton.tsx @@ -79,7 +79,10 @@ export function ExecuteInstructionButton({ await executeTransaction(rpcContext, proposal, proposalInstruction) await fetchRealm(realmInfo?.programId, realmInfo?.realmId) } catch (error) { - notify({ type: 'error', message: `error executing instruction ${error}` }) + notify({ + type: 'error', + message: `error executing instruction ${error}`, + }) console.log('error executing instruction', error) setPlaying(PlayState.Error) diff --git a/hooks/useGovernanceAssets.ts b/hooks/useGovernanceAssets.ts index eb44fe9588..56ce393d0d 100644 --- a/hooks/useGovernanceAssets.ts +++ b/hooks/useGovernanceAssets.ts @@ -5,13 +5,49 @@ import { GovernedMintInfoAccount, parseMintAccountData, } from '@utils/tokens' -import { Instructions } from '@utils/uiTypes/proposalCreationTypes' +import { + InstructionEnum, + PackageEnum, +} from '@utils/uiTypes/proposalCreationTypes' import useWalletStore from 'stores/useWalletStore' import useRealm from './useRealm' import useGovernanceAssetsStore from 'stores/useGovernanceAssetsStore' +export type InstructionTag = 'beta' | 'deprecated' + +type Instruction = { + name: string + isVisible?: boolean + packageId: PackageEnum + tag?: InstructionTag +} + +type Instructions = { + [instructionId in InstructionEnum]: Instruction +} + +export type InstructionType = { + id: InstructionEnum + name: string + packageId: PackageEnum + tag?: InstructionTag +} + +type Package = { + name: string + image?: string +} + +type Packages = { + [packageId in PackageEnum]: Package +} + +export type PackageType = Package & { + id: PackageEnum +} + export default function useGovernanceAssets() { const { ownVoterWeight, realm, symbol, governances } = useRealm() const connection = useWalletStore((s) => s.connection.current) @@ -92,10 +128,6 @@ export default function useGovernanceAssets() { ownVoterWeight.canCreateProposal(gov.account.config) ) - const getAvailableInstructions = () => { - return availableInstructions.filter((itx) => itx.isVisible) - } - async function getMintWithGovernances() { const mintGovernances = getGovernancesByAccountTypes([ GovernanceAccountType.MintGovernanceV1, @@ -136,200 +168,260 @@ export default function useGovernanceAssets() { ownVoterWeight.canCreateProposal(acc.governance?.account?.config) ) - const availableInstructions = [ - { - id: Instructions.TribecaCreateEpochGauge, + const packages: Packages = { + [PackageEnum.Native]: { + name: 'Native', + }, + [PackageEnum.VoteStakeRegistry]: { + name: 'Vote Stake Registry', + image: '/img/vote-stake-registry.png', + }, + [PackageEnum.Solend]: { + name: 'Solend', + image: '/img/solend.png', + }, + [PackageEnum.Raydium]: { + name: 'Raydium', + image: '/img/raydium.png', + }, + [PackageEnum.UXD]: { + name: 'UXD', + image: '/img/uxd.png', + }, + [PackageEnum.Friktion]: { + name: 'Friktion', + image: '/img/friktion.png', + }, + [PackageEnum.Tribeca]: { + name: 'Tribeca', + image: '/img/tribeca.png', + }, + } + + const instructions: Instructions = { + [InstructionEnum.TribecaCreateEpochGauge]: { name: 'Tribeca: Create Epoch Gauge', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Tribeca, }, - { - id: Instructions.TribecaCreateEscrowGovernanceTokenATA, + [InstructionEnum.TribecaCreateEscrowGovernanceTokenATA]: { name: 'Tribeca: Create Escrow Governance Token ATA', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Tribeca, }, - { - id: Instructions.TribecaCreateGaugeVote, + [InstructionEnum.TribecaCreateGaugeVote]: { name: 'Tribeca: Create Gauge Vote', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Tribeca, }, - { - id: Instructions.TribecaCreateGaugeVoter, + [InstructionEnum.TribecaCreateGaugeVoter]: { name: 'Tribeca: Create Gauge Voter', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Tribeca, }, - { - id: Instructions.TribecaGaugeCommitVote, + [InstructionEnum.TribecaGaugeCommitVote]: { name: 'Tribeca: Gauge Commit Vote', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Tribeca, }, - { - id: Instructions.TribecaGaugeRevertVote, + [InstructionEnum.TribecaGaugeRevertVote]: { name: 'Tribeca: Gauge Revert Vote', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Tribeca, }, - { - id: Instructions.TribecaLock, + [InstructionEnum.TribecaLock]: { name: 'Tribeca: Lock Tokens', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Tribeca, }, - { - id: Instructions.TribecaNewEscrow, + [InstructionEnum.TribecaNewEscrow]: { name: 'Tribeca: New Escrow', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Tribeca, }, - { - id: Instructions.TribecaPrepareEpochGaugeVoter, + [InstructionEnum.TribecaPrepareEpochGaugeVoter]: { name: 'Tribeca: Prepare Epoch Gauge Voter', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Tribeca, }, - { - id: Instructions.TribecaResetEpochGaugeVoter, + [InstructionEnum.TribecaResetEpochGaugeVoter]: { name: 'Tribeca: Reset Epoch Gauge Voter', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Tribeca, }, - { - id: Instructions.TribecaGaugeSetVote, + [InstructionEnum.TribecaGaugeSetVote]: { name: 'Tribeca: Set Gauge Vote', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Tribeca, }, - { - id: Instructions.SolendCreateObligationAccount, - name: 'Solend: Create Obligation Account', + [InstructionEnum.SolendCreateObligationAccount]: { + name: 'Create Obligation Account', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Solend, }, - { - id: Instructions.SolendInitObligationAccount, - name: 'Solend: Init Obligation Account', + [InstructionEnum.SolendInitObligationAccount]: { + name: 'Init Obligation Account', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Solend, }, - { - id: Instructions.SolendDepositReserveLiquidityAndObligationCollateral, - name: 'Solend: Deposit Funds', + [InstructionEnum.SolendDepositReserveLiquidityAndObligationCollateral]: { + name: 'Deposit Funds', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Solend, }, - { - id: Instructions.SolendRefreshReserve, - name: 'Solend: Refresh Reserve', + [InstructionEnum.SolendRefreshReserve]: { + name: 'Refresh Reserve', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Solend, }, - { - id: Instructions.SolendRefreshObligation, - name: 'Solend: Refresh Obligation', + [InstructionEnum.SolendRefreshObligation]: { + name: 'Refresh Obligation', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Solend, }, - { - id: - Instructions.SolendWithdrawObligationCollateralAndRedeemReserveLiquidity, - name: 'Solend: Withdraw Funds', + [InstructionEnum.SolendWithdrawObligationCollateralAndRedeemReserveLiquidity]: { + name: 'Withdraw Funds', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Solend, }, - { - id: Instructions.UXDInitializeController, - name: 'UXD: Initialize Controller', + [InstructionEnum.UXDInitializeController]: { + name: 'Initialize Controller', isVisible: canUseUxdInstructions, + packageId: PackageEnum.UXD, }, - { - id: Instructions.UXDSetRedeemableGlobalSupplyCap, - name: 'UXD: Set Redeemable Global Supply Cap', + [InstructionEnum.UXDSetRedeemableGlobalSupplyCap]: { + name: 'Set Redeemable Global Supply Cap', isVisible: canUseUxdInstructions, + packageId: PackageEnum.UXD, }, - { - id: Instructions.UXDSetMangoDepositoriesRedeemableSoftCap, - name: 'UXD: Set Mango Depositories Redeemable Supply Soft Cap', + [InstructionEnum.UXDSetMangoDepositoriesRedeemableSoftCap]: { + name: 'Set Mango Depositories Redeemable Supply Soft Cap', isVisible: canUseUxdInstructions, + packageId: PackageEnum.UXD, }, - { - id: Instructions.UXDRegisterMangoDepository, - name: 'UXD: Register Mango Depository', + [InstructionEnum.UXDRegisterMangoDepository]: { + name: 'Register Mango Depository', isVisible: canUseUxdInstructions, + packageId: PackageEnum.UXD, }, - { - id: Instructions.UXDDepositInsuranceToMangoDepository, - name: 'UXD: Deposit Insurance To Mango Depository', + [InstructionEnum.UXDDepositInsuranceToMangoDepository]: { + name: 'Deposit Insurance To Mango Depository', isVisible: canUseUxdInstructions, + packageId: PackageEnum.UXD, }, - { - id: Instructions.UXDWithdrawInsuranceFromMangoDepository, - name: 'UXD: Withdraw Insurance From Mango Depository', + [InstructionEnum.UXDWithdrawInsuranceFromMangoDepository]: { + name: 'Withdraw Insurance From Mango Depository', isVisible: canUseUxdInstructions, + packageId: PackageEnum.UXD, + }, + [InstructionEnum.RaydiumAddLiquidity]: { + name: 'Add To Liquidity Pool', + isVisible: canUseAnyInstruction, + packageId: PackageEnum.Raydium, }, - { - id: Instructions.RaydiumAddLiquidity, - name: 'Raydium: Add To Liquidity Pool', + [InstructionEnum.RaydiumRemoveLiquidity]: { + name: 'Remove From Liquidity Pool', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Raydium, }, - { - id: Instructions.RaydiumRemoveLiquidity, - name: 'Raydium: Remove From Liquidity Pool', + [InstructionEnum.FriktionDepositIntoVolt]: { + name: 'Deposit into Volt', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Friktion, }, - { - id: Instructions.Transfer, + [InstructionEnum.Transfer]: { name: 'Transfer Tokens', isVisible: canUseTokenTransferInstruction, + packageId: PackageEnum.Native, + tag: 'deprecated', }, - { - id: Instructions.Grant, + [InstructionEnum.Grant]: { name: 'Grant', isVisible: canUseTokenTransferInstruction && realm?.account.config.useCommunityVoterWeightAddin, + packageId: PackageEnum.VoteStakeRegistry, + tag: 'deprecated', }, - { - id: Instructions.Clawback, + [InstructionEnum.Clawback]: { name: 'Clawback', isVisible: canUseTokenTransferInstruction && realm?.account.config.useCommunityVoterWeightAddin, + packageId: PackageEnum.VoteStakeRegistry, + tag: 'deprecated', }, - { - id: Instructions.CreateAssociatedTokenAccount, + [InstructionEnum.CreateAssociatedTokenAccount]: { name: 'Create Associated Token Account', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Native, }, - { - id: Instructions.FriktionDepositIntoVolt, - name: 'Friktion: Deposit into Volt', - isVisible: canUseAnyInstruction, - }, - { - id: Instructions.ProgramUpgrade, + [InstructionEnum.ProgramUpgrade]: { name: 'Upgrade Program', isVisible: canUseProgramUpgradeInstruction, + packageId: PackageEnum.Native, }, - { - id: Instructions.SetProgramAuthority, + [InstructionEnum.SetProgramAuthority]: { name: 'Set Program Authority', isVisible: canUseProgramUpgradeInstruction, + packageId: PackageEnum.Native, }, - { - id: Instructions.Mint, + [InstructionEnum.Mint]: { name: 'Mint Tokens', isVisible: canUseMintInstruction, + packageId: PackageEnum.Native, + tag: 'deprecated', }, - { - id: Instructions.Base64, + [InstructionEnum.Base64]: { name: 'Execute Custom Instruction', isVisible: canUseAnyInstruction, + packageId: PackageEnum.Native, }, - { - id: Instructions.None, + [InstructionEnum.None]: { name: 'None', - isVisible: + isVisible: !!( realm && Object.values(governances).some((g) => ownVoterWeight.canCreateProposal(g.account.config) - ), + ) + ), + packageId: PackageEnum.Native, }, - ] + } + + const availableInstructions = Object.entries(instructions) + .filter( + ([, { isVisible }]) => typeof isVisible === 'undefined' || isVisible + ) + .map(([id, { name, packageId, tag }]) => ({ + id: Number(id) as InstructionEnum, + name, + packageId, + tag, + })) + + const availablePackages: PackageType[] = Object.entries(packages).map( + ([id, infos]) => ({ + id: Number(id) as PackageEnum, + ...infos, + }) + ) + + const getPackageTypeById = (packageId: PackageEnum) => { + return availablePackages.find( + (availablePackage) => availablePackage.id === packageId + ) + } + return { governancesArray, getGovernancesByAccountType, getGovernancesByAccountTypes, + availablePackages, availableInstructions, - getAvailableInstructions, governedTokenAccounts, getMintWithGovernances, + getPackageTypeById, canUseTransferInstruction, canUseMintInstruction, canMintRealmCommunityToken, diff --git a/hooks/useHotWalletPluginTokenAccounts.ts b/hooks/useHotWalletPluginTokenAccounts.ts index 25ee1aee73..f5f3826f6e 100644 --- a/hooks/useHotWalletPluginTokenAccounts.ts +++ b/hooks/useHotWalletPluginTokenAccounts.ts @@ -92,7 +92,7 @@ const useHotWalletPluginTokenAccounts = ( ), } }) - .sort((a, b) => b.amount.toNumber() - a.amount.toNumber()) + .sort((a, b) => (b.amount.toString() < a.amount.toString() ? -1 : 1)) ) }, [ connection, diff --git a/hooks/useInstructionFormBuilder.ts b/hooks/useInstructionFormBuilder.ts index 062f1d3c61..3c98c762da 100644 --- a/hooks/useInstructionFormBuilder.ts +++ b/hooks/useInstructionFormBuilder.ts @@ -5,13 +5,15 @@ import { Connection, PublicKey, TransactionInstruction } from '@solana/web3.js' import { debounce } from '@utils/debounce' import { isFormValid } from '@utils/formValidation' import { GovernedMultiTypeAccount } from '@utils/tokens' -import { UiInstruction } from '@utils/uiTypes/proposalCreationTypes' +import { FormInstructionData } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from 'pages/dao/[symbol]/proposal/new' import useWalletStore from 'stores/useWalletStore' import { SignerWalletAdapter } from '@solana/wallet-adapter-base' import useGovernedMultiTypeAccounts from './useGovernedMultiTypeAccounts' +export type SerializedInstruction = string + function useInstructionFormBuilder< T extends { governedAccount?: GovernedMultiTypeAccount @@ -21,8 +23,9 @@ function useInstructionFormBuilder< initialFormValues, schema, buildInstruction, + getCustomHoldUpTime, }: { - index?: number + index: number initialFormValues: T schema: yup.ObjectSchema< { @@ -39,11 +42,12 @@ function useInstructionFormBuilder< connection: Connection wallet: SignerWalletAdapter governedAccountPubkey: PublicKey - }) => Promise + }) => Promise + getCustomHoldUpTime?: () => Promise }) { const connection = useWalletStore((s) => s.connection) const wallet = useWalletStore((s) => s.current) - const { handleSetInstructions } = useContext(NewProposalContext) + const { handleSetInstruction } = useContext(NewProposalContext) const { getGovernedAccountPublicKey } = useGovernedMultiTypeAccounts() const [form, setForm] = useState(initialFormValues) @@ -60,7 +64,7 @@ function useInstructionFormBuilder< return isValid } - const getInstruction = async (): Promise => { + const getInstruction = async (): Promise => { const governedAccountPubkey = getGovernedAccountPublicKey( form.governedAccount, true @@ -78,22 +82,35 @@ function useInstructionFormBuilder< } } try { - return { - serializedInstruction: buildInstruction - ? serializeInstructionToBase64( - await buildInstruction({ - form, - connection: connection.current, - wallet, - governedAccountPubkey, - }) + const transactionInstructionOrSerializedInstruction = buildInstruction + ? await buildInstruction({ + form, + connection: connection.current, + wallet, + governedAccountPubkey, + }) + : '' + + const serializedInstruction = + typeof transactionInstructionOrSerializedInstruction === 'string' + ? transactionInstructionOrSerializedInstruction + : serializeInstructionToBase64( + transactionInstructionOrSerializedInstruction ) - : '', + + const customHoldUpTime = getCustomHoldUpTime + ? await getCustomHoldUpTime() + : undefined + + return { + serializedInstruction, isValid: true, governance: form.governedAccount?.governance, + customHoldUpTime, } } catch (e) { console.error(e) + return { serializedInstruction: '', isValid: false, @@ -114,7 +131,7 @@ function useInstructionFormBuilder< debounce.debounceFcn(async () => { await validateForm() }) - handleSetInstructions( + handleSetInstruction( { governedAccount: form.governedAccount?.governance, getInstruction }, index ) diff --git a/pages/dao/[symbol]/proposal/components/DryRunInstructionBtn.tsx b/pages/dao/[symbol]/proposal/components/DryRunInstructionBtn.tsx index 9a3e62c3ea..eb1c620e41 100644 --- a/pages/dao/[symbol]/proposal/components/DryRunInstructionBtn.tsx +++ b/pages/dao/[symbol]/proposal/components/DryRunInstructionBtn.tsx @@ -5,7 +5,7 @@ import Modal from '@components/Modal' import { getInstructionDataFromBase64 } from '@solana/spl-governance' import { SimulatedTransactionResponse, Transaction } from '@solana/web3.js' import { notify } from '@utils/notifications' -import { UiInstruction } from '@utils/uiTypes/proposalCreationTypes' +import { FormInstructionData } from '@utils/uiTypes/proposalCreationTypes' import { dryRunInstruction } from 'actions/dryRunInstruction' import React, { useState } from 'react' import useWalletStore from 'stores/useWalletStore' @@ -14,7 +14,7 @@ const DryRunInstructionBtn = ({ getInstructionDataFcn, btnClassNames, }: { - getInstructionDataFcn: (() => Promise) | undefined + getInstructionDataFcn: (() => Promise) | undefined btnClassNames: string }) => { const connection = useWalletStore((s) => s.connection) diff --git a/pages/dao/[symbol]/proposal/components/instructions/Execute.tsx b/pages/dao/[symbol]/proposal/components/Execute.tsx similarity index 100% rename from pages/dao/[symbol]/proposal/components/instructions/Execute.tsx rename to pages/dao/[symbol]/proposal/components/Execute.tsx diff --git a/pages/dao/[symbol]/proposal/components/InstructionContentContainer.tsx b/pages/dao/[symbol]/proposal/components/InstructionContentContainer.tsx index 14f4108815..c71be64c2e 100644 --- a/pages/dao/[symbol]/proposal/components/InstructionContentContainer.tsx +++ b/pages/dao/[symbol]/proposal/components/InstructionContentContainer.tsx @@ -1,29 +1,25 @@ import { ComponentInstructionData, - Instructions, + InstructionEnum, } from '@utils/uiTypes/proposalCreationTypes' -import React from 'react' +import React, { ReactNode } from 'react' import DryRunInstructionBtn from './DryRunInstructionBtn' const InstructionContentContainer = ({ children, - idx, - instructionsData, + instruction, }: { - children: any - idx: number - instructionsData: ComponentInstructionData[] + children: ReactNode + instruction?: ComponentInstructionData }) => { - const currentInstruction = instructionsData ? instructionsData[idx] : null - return (
{children} - {currentInstruction?.type?.id !== Instructions.None && ( + {instruction?.type?.id !== InstructionEnum.None && ( )}
diff --git a/pages/dao/[symbol]/proposal/components/VoteBySwitch.tsx b/pages/dao/[symbol]/proposal/components/VoteBySwitch.tsx index fc94d3a5ea..7160f1fd99 100644 --- a/pages/dao/[symbol]/proposal/components/VoteBySwitch.tsx +++ b/pages/dao/[symbol]/proposal/components/VoteBySwitch.tsx @@ -5,9 +5,9 @@ import React from 'react' const VoteBySwitch = ({ checked, onChange }) => { const { toManyCouncilOutstandingProposalsForUse } = useRealm() return !toManyCouncilOutstandingProposalsForUse ? ( -
-
Vote by council
-
+
+
Vote by council
+
diff --git a/pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx b/pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx deleted file mode 100644 index 7e2378116f..0000000000 --- a/pages/dao/[symbol]/proposal/components/instructions/CustomBase64.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react' -import * as yup from 'yup' -import { - getInstructionDataFromBase64, - Governance, - ProgramAccount, -} from '@solana/spl-governance' -import Input from '@components/inputs/Input' -import Textarea from '@components/inputs/Textarea' -import { validateInstruction } from '@utils/instructionTools' -import { - Base64InstructionForm, - UiInstruction, -} from '@utils/uiTypes/proposalCreationTypes' -import useWalletStore from 'stores/useWalletStore' - -import { NewProposalContext } from '../../new' -import GovernedAccountSelect from '../GovernedAccountSelect' -import useGovernedMultiTypeAccounts from '@hooks/useGovernedMultiTypeAccounts' - -const CustomBase64 = ({ - index, - governance, -}: { - index: number - governance: ProgramAccount | null -}) => { - const wallet = useWalletStore((s) => s.current) - const { governedMultiTypeAccounts } = useGovernedMultiTypeAccounts() - const shouldBeGoverned = index !== 0 && governance - const [form, setForm] = useState({ - governedAccount: undefined, - base64: '', - holdUpTime: 0, - }) - const [formErrors, setFormErrors] = useState({}) - const { handleSetInstructions } = useContext(NewProposalContext) - const handleSetForm = ({ propertyName, value }) => { - setFormErrors({}) - setForm({ ...form, [propertyName]: value }) - } - async function getInstruction(): Promise { - const isValid = await validateInstruction({ schema, form, setFormErrors }) - let serializedInstruction = '' - if ( - isValid && - form.governedAccount?.governance?.account && - wallet?.publicKey - ) { - serializedInstruction = form.base64 - } - const obj: UiInstruction = { - serializedInstruction: serializedInstruction, - isValid, - governance: form.governedAccount?.governance, - customHoldUpTime: form.holdUpTime, - } - return obj - } - useEffect(() => { - handleSetInstructions( - { governedAccount: form.governedAccount?.governance, getInstruction }, - index - ) - }, [form]) - const schema = yup.object().shape({ - governedAccount: yup - .object() - .nullable() - .required('Governed account is required'), - base64: yup - .string() - .required('Instruction is required') - .test('base64Test', 'Invalid base64', function (val: string) { - if (val) { - try { - getInstructionDataFromBase64(val) - return true - } catch (e) { - return false - } - } else { - return this.createError({ - message: `Instruction is required`, - }) - } - }), - }) - const validateAmountOnBlur = () => { - const value = form.holdUpTime - - handleSetForm({ - value: parseFloat( - Math.max( - Number(0), - Math.min(Number(Number.MAX_SAFE_INTEGER), Number(value)) - ).toFixed() - ), - propertyName: 'holdUpTime', - }) - } - return ( - <> - { - handleSetForm({ value, propertyName: 'governedAccount' }) - }} - value={form.governedAccount} - error={formErrors['governedAccount']} - shouldBeGoverned={shouldBeGoverned} - governance={governance} - /> - { - handleSetForm({ - value: event.target.value, - propertyName: 'holdUpTime', - }) - }} - step={1} - error={formErrors['holdUpTime']} - onBlur={validateAmountOnBlur} - /> - - - ) -} - -export default CustomBase64 diff --git a/pages/dao/[symbol]/proposal/components/instructions/Friktion/FriktionDeposit.tsx b/pages/dao/[symbol]/proposal/components/instructions/Friktion/FriktionDeposit.tsx index 0759162e93..0f56a9bbe9 100644 --- a/pages/dao/[symbol]/proposal/components/instructions/Friktion/FriktionDeposit.tsx +++ b/pages/dao/[symbol]/proposal/components/instructions/Friktion/FriktionDeposit.tsx @@ -8,7 +8,7 @@ import useWalletStore from 'stores/useWalletStore' import { GovernedMultiTypeAccount } from '@utils/tokens' import { FriktionDepositForm, - UiInstruction, + FormInstructionData, } from '@utils/uiTypes/proposalCreationTypes' import { NewProposalContext } from '../../../new' import { getFriktionDepositSchema } from '@utils/validations' @@ -52,7 +52,7 @@ const FriktionDeposit = ({ ? getMintMinAmountAsDecimal(form.mintInfo) : 1 const currentPrecision = precision(mintMinAmount) - const { handleSetInstructions } = useContext(NewProposalContext) + const { handleSetInstruction } = useContext(NewProposalContext) const handleSetForm = ({ propertyName, value }) => { setFormErrors({}) setForm({ ...form, [propertyName]: value }) @@ -81,7 +81,7 @@ const FriktionDeposit = ({ }) } - async function getInstruction(): Promise { + async function getInstruction(): Promise { return getFriktionDepositInstruction({ schema, form, @@ -112,7 +112,7 @@ const FriktionDeposit = ({ }) }, [realmInfo?.programId]) useEffect(() => { - handleSetInstructions( + handleSetInstruction( { governedAccount: governedAccount, getInstruction }, index ) diff --git a/pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx b/pages/dao/[symbol]/proposal/components/instructions/Native/CreateAssociatedTokenAccount.tsx similarity index 100% rename from pages/dao/[symbol]/proposal/components/instructions/CreateAssociatedTokenAccount.tsx rename to pages/dao/[symbol]/proposal/components/instructions/Native/CreateAssociatedTokenAccount.tsx diff --git a/pages/dao/[symbol]/proposal/components/instructions/Native/CustomBase64.tsx b/pages/dao/[symbol]/proposal/components/instructions/Native/CustomBase64.tsx new file mode 100644 index 0000000000..2c28985a51 --- /dev/null +++ b/pages/dao/[symbol]/proposal/components/instructions/Native/CustomBase64.tsx @@ -0,0 +1,113 @@ +import React from 'react' +import * as yup from 'yup' +import { getInstructionDataFromBase64 } from '@solana/spl-governance' +import Input from '@components/inputs/Input' +import Textarea from '@components/inputs/Textarea' +import { Base64InstructionForm } from '@utils/uiTypes/proposalCreationTypes' +import { GovernedMultiTypeAccount } from '@utils/tokens' +import useInstructionFormBuilder, { + SerializedInstruction, +} from '@hooks/useInstructionFormBuilder' + +const CustomBase64 = ({ + index, + governedAccount, +}: { + index: number + governedAccount?: GovernedMultiTypeAccount +}) => { + const { + form, + formErrors, + handleSetForm, + } = useInstructionFormBuilder({ + index, + initialFormValues: { + governedAccount, + base64: '', + holdUpTime: 0, + }, + schema: yup.object().shape({ + governedAccount: yup + .object() + .nullable() + .required('Governed account is required'), + base64: yup + .string() + .required('Instruction is required') + .test('base64Test', 'Invalid base64', function (val: string) { + if (val) { + try { + getInstructionDataFromBase64(val) + return true + } catch (e) { + return false + } + } + + return this.createError({ + message: `Instruction is required`, + }) + }), + holdUpTime: yup.number().required('Hold up time is required'), + }), + + getCustomHoldUpTime: async function () { + return form.holdUpTime + }, + + buildInstruction: async function () { + return form.base64 as SerializedInstruction + }, + }) + + const validateAmountOnBlur = () => { + const value = form.holdUpTime + + handleSetForm({ + value: parseFloat( + Math.max( + Number(0), + Math.min(Number(Number.MAX_SAFE_INTEGER), Number(value)) + ).toFixed() + ), + propertyName: 'holdUpTime', + }) + } + + return ( + <> + { + handleSetForm({ + value: event.target.value, + propertyName: 'holdUpTime', + }) + }} + step={1} + error={formErrors['holdUpTime']} + onBlur={validateAmountOnBlur} + /> + + - {canChooseWhoVote && ( - { - setVoteByCouncil(!voteByCouncil) - }} - > - )} - +
+ +