From 0df8439157b5ec7aeb2425d8c82101821d71ceb4 Mon Sep 17 00:00:00 2001 From: Thomas Proust Date: Tue, 30 Nov 2021 16:20:12 +0900 Subject: [PATCH 01/15] 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 02/15] 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 03/15] 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 04/15] 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 05/15] 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 06/15] 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 07/15] 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 08/15] 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 09/15] 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 10/15] 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 11/15] 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 12/15] 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 13/15] 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 14/15] 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 zcmeI3-HTq;6~@mP9UV>5B&Ii`$bhIxM2r_nFN7F|6vi9DFa-@LRWxV{6(yJmE$vNP z3pEs)(iTb)tHxAY0#yq_Q(LGl)*|#q?@RxU^Y-`b@2_*-^A6Li`r?|s_FB(+*4iIu zpO0YIEpK^^C+n#fbz}y*=cIBVa99!*gE?vHb7j2_}9zfi!RpQ#C}38Ar0$?m}KdE<3qI zEAf5#?#jqBN;;t*6bCQ+kp<49$lJ&(PQNC4t;Th9jq?d3>{kM&q%Qjbm@9BBbFISH zUqCl^+8og);@aK=o|s;>Eax%Pdc^I5=XbPU%_AB)+4#X46mxc+ARdl>SI%L3f+lbW zF=zC$jho%uit|w+)+?s<9UI9Q?sYXxng>OL*SriJ98IF@ZX8Jd9uh9V)fLL zvDKXdDsIG`54lr4_6p}me>C@qwzimL1LiCu!H`>P`;v)mu#*2B{BITVE`2>R=f8xv zRUYHed@J@X=04^8ttCncu+Rm2Sw4BZ7uzAj!}`e=ejX5z)*~}`pZo5BcZ$!E+C3FD zUrOzk+MEZ67=y-QpMx_{-U+$;na%CV9Sr{^YjTdn&-~~46Kdk@JdD)fa=!=9p(K>a zj7eht1mWEH%={;wxDXhV8nU%ec(UGkZcpZNP+OhDVU!;pdvA*+dj?ZWs|IX?>m7s5 z^`(2{SpM_)^%}{~7humC-;+o43+`R^gr1q3xl_Di^b3u zTsG&)a;G86AI=}oUt)Q#{tdWneJzQN@Nu$%(=+&~z!9853Hg~b%~O8<25(frjNE^6 zUiOOK7sLBy@CL>5stfJ{>s73+{3+P#@csyVxc}A&8>ZZ2F@^KZ3zauGwYw)oLzfOG1~9hb61ItmDmcRpSds{vwqr> zx-P^g-JDUhVgJb{AN2~OXBfBFEU*6?`MqVNEx>T{LCN< zBDrPH%<6cXb!6@wY_Z}gf8j7F9kD;o8*$R|9cm02U^15n`ZlF9wltFiCD@QOvt=C% zX_$(Mj@>3UWct5Q^qu4|@^xgXTjTGdKRb!rK9zwmD2o<0f*;`=ag7kpb#0AyYf;T& zpjwnI#Roko%`8phcu3(5U8{s{lX+{#?d7c5YKyH?cJ2@Q7Q2cZLmolSATKyM!~N?~ z^yA1aNcEj*>WGJGh?V3_O?+acmfgr>JPUk4`)}kE8|OCSspr9e+yvjTLZ<_2fJwB+f-{?W2px zb9rpt3v4LhweSCloCx+hq_!*sPQ%l{R5!Kg9;f?s_V4{^bg6?qg{c1aHA_IArDRC^ z(~)VE*Hr`i=TEdxdRyLFexC)Lu3MeIXLH;@&j${;x5G>=JTLWn=4(8+R`suf`yBZt z^6iG`Ic=Nozvf$#zD+&T$fp{9f&2%#W!`*%c>rPW^v`nrK8s$;``Gz**0`?ckq`Lc zx*idy&bu<7@Ba(m@tPvv0ATc<@kWia7JU1V_YuvDIh%3@7uht{tl~h|1|cmC31hpfjbtJW0Sj z&-MNtgY${%AdNxmp!?%1$W0B%sEHV}XUL`Zi2V`VP73}UYxF|IwKZ_6|GRB(ZUwu~ zt^2poCyc??29`b3pQX+AOwO)e91GZU$o%^4HCybT$W!Kk^KMs%)?x23(nf;udq(G> z{kO=x)G~$XgMArk?4KMCM&AzJ%&p@+@OCe8F0po8(~P1q5a!&UGwhjJvS-HmnI^FU(&L*}0Sk!Dr8zxqBv!vbt(PK0%H(rX>piv&tNPMtgha2%TXD zV%)i8&hl_BVfTCHsxX0Su3~c$vxexq%W55x*8@gleFr&TW9M6l3+EE^a1;FrdnT`; z%mRn*`MS5w`B#iE-mkIWJ_Wy0a<%$4D%L=pzsKoZnsCk(tWbG2;%`9o-QclcEa1^@ zqrmP&wBJsY(5Zc5buRq{`G<5H=c|e=wI%wb5!-lv&M~om?2eGQ$2r^eeTwtM{^sQp zD#Z}{L*yqee0mdW(B4xGBj;wCF{U8qW<=-Q{drXdzUW2QS7z|xRRCsyCc zRKu;ABkRE2ASXue0Z$`(Mwsyao{^dkaiRX&J8mI%{QW(IH;Q*YICSmkzM=DIfp3MD zlb9{luUyEh$Q5LzuCTU*;S>t-UqU`aUPSm#JN*ZK;rfZWPPsUHb>@V7$n>l=5}rE{ z&4tD=c^$yo`bVZKby4#&G5XG@TqEBP>l)`-;L{ki4%$ocdPoaMNw9;PIREDTQRl^L z$j69s4Kar-a{&Qq6*#q*bUo;rz8|@zaNV z(YdCxS@)ObeH!~<{rI$36nhcTv%zV^e(RiCjGw3}ZNf<&ovm8$#}VD9KSj3O?3yN7R~J{atwI{(RYC7jqpy#81-G_0YuN|%yok# zOE|SN!ZYHU%I((set, 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 15/15] 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,