Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/Marinade native support #3

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import VoteBySwitch from 'pages/dao/[symbol]/proposal/components/VoteBySwitch'
import Button from '@components/Button'
import Tooltip from '@components/Tooltip'
import { getStakeSchema } from '@utils/validations'
import { getConvertToMsolInstruction } from '@utils/instructionTools'
import {
getConvertToMsolInstruction,
getMarinadeNativeStakeInstruction,
} from '@utils/instructionTools'
import { getInstructionDataFromBase64 } from '@solana/spl-governance'
import useQueryContext from '@hooks/useQueryContext'
import { useRouter } from 'next/router'
Expand All @@ -27,12 +30,16 @@ import { AssetAccount } from '@utils/uiTypes/assets'
import useWalletOnePointOh from '@hooks/useWalletOnePointOh'
import { useRealmQuery } from '@hooks/queries/realm'
import useLegacyConnectionContext from '@hooks/useLegacyConnectionContext'
import { ButtonToggle } from '@hub/components/controls/ButtonToggle'
import { InstructionDataWithHoldUpTime } from 'actions/createProposal'

const ConvertToMsol = () => {
const MarinadeModal = () => {
const realm = useRealmQuery().data?.result
const { canChooseWhoVote, symbol } = useRealm()
const { canUseTransferInstruction } = useGovernanceAssets()
const { governedTokenAccounts } = useGovernanceAssets()
const {
canUseTransferInstruction,
governedTokenAccounts,
} = useGovernanceAssets()
const { fmtUrlWithCluster } = useQueryContext()
const router = useRouter()
const { handleCreateProposal } = useCreateProposal()
Expand All @@ -50,6 +57,7 @@ const ConvertToMsol = () => {
title: '',
description: '',
})
const [isNative, setIsNative] = useState(false)
const [showOptions, setShowOptions] = useState(false)
const [voteByCouncil, setVoteByCouncil] = useState(false)
const [isLoading, setIsLoading] = useState(false)
Expand All @@ -64,7 +72,9 @@ const ConvertToMsol = () => {
form.governedTokenAccount.extensions.mint.account
)
: 1
const proposalTitle = `Convert ${form.amount} SOL to mSOL`
const proposalTitle = isNative
? `Stake ${form.amount} SOL with Marinade Native`
: `Convert ${form.amount} SOL to mSOL`
const schema = getStakeSchema({ form })

const handleSetForm = ({ propertyName, value }) => {
Expand All @@ -76,15 +86,27 @@ const ConvertToMsol = () => {
if (currentAccount?.governance === undefined) throw new Error()

setIsLoading(true)
const instruction: UiInstruction = await getConvertToMsolInstruction({
schema,
form,
connection,
wallet,
setFormErrors,
})
let instructions: UiInstruction[] = []
if (isNative) {
instructions = await getMarinadeNativeStakeInstruction({
schema,
form,
wallet,
setFormErrors,
})
} else {
instructions.push(
await getConvertToMsolInstruction({
schema,
form,
connection,
wallet,
setFormErrors,
})
)
}

if (instruction.isValid) {
if (instructions.every((i) => i?.isValid)) {
if (!realm) {
setIsLoading(false)
throw 'No realm selected'
Expand All @@ -93,20 +115,22 @@ const ConvertToMsol = () => {
const governance = currentAccount?.governance
const holdUpTime = governance?.account?.config.minInstructionHoldUpTime

const instructionData = {
data: instruction.serializedInstruction
? getInstructionDataFromBase64(instruction.serializedInstruction)
: null,
holdUpTime: holdUpTime,
prerequisiteInstructions: instruction.prerequisiteInstructions || [],
}
const instructionsData: InstructionDataWithHoldUpTime[] = instructions.map(
(instruction) => ({
data: instruction.serializedInstruction
? getInstructionDataFromBase64(instruction.serializedInstruction)
: null,
holdUpTime: holdUpTime,
prerequisiteInstructions: instruction.prerequisiteInstructions || [],
})
)

try {
const proposalAddress = await handleCreateProposal({
title: form.title ? form.title : proposalTitle,
description: form.description ? form.description : '',
title: form.title && form.title !== '' ? form.title : proposalTitle,
description: form.description ?? '',
governance: currentAccount?.governance,
instructionsData: [instructionData],
instructionsData: instructionsData,
voteByCouncil,
isDraft: false,
})
Expand All @@ -131,10 +155,17 @@ const ConvertToMsol = () => {

return (
<>
<h3 className="mb-4 flex items-center">Convert SOL to mSOL</h3>
<h3 className="mb-4 flex items-center">Marinade Staking</h3>
<ButtonToggle
value={isNative}
valueFalseText="Liquid Staking"
valueTrueText="Native Staking"
onChange={setIsNative}
className="mb-4"
/>
<AccountLabel></AccountLabel>
<div className="space-y-4 w-full pb-4">
{mSolTokenAccounts.length > 0 && (
{!isNative && mSolTokenAccounts.length > 0 && (
<GovernedAccountSelect
label="mSOL Treasury account"
governedAccounts={mSolTokenAccounts as AssetAccount[]}
Expand All @@ -149,7 +180,7 @@ const ConvertToMsol = () => {
}
error={formErrors['destinationAccount']}
noMaxWidth={true}
></GovernedAccountSelect>
/>
)}
<Input
min={mintMinAmount}
Expand Down Expand Up @@ -250,4 +281,4 @@ const ConvertToMsol = () => {
)
}

export default ConvertToMsol
export default MarinadeModal
26 changes: 26 additions & 0 deletions components/instructions/programs/memo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Connection } from '@solana/web3.js'
import { AccountMetaData } from '@solana/spl-governance'
import * as BufferLayout from '@solana/buffer-layout'

export const MEMO_INSTRUCTIONS = {
MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr: {
123: {
name: 'Memo Program v2',
accounts: [{ name: 'Stake Account' }],
getDataUI: async (
_connection: Connection,
_data: Uint8Array,
_accounts: AccountMetaData[]
) => {
const layout = BufferLayout.utf8(_data.length, 'memoData')
const decodedData = layout.decode(Buffer.from(_data))

return (
<>
<p>Memo: {decodedData}</p>
</>
)
},
},
},
}
37 changes: 36 additions & 1 deletion components/instructions/programs/stake.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,44 @@
import { Connection, LAMPORTS_PER_SOL } from '@solana/web3.js'
import { Connection, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'
import { AccountMetaData } from '@solana/spl-governance'
import * as BufferLayout from '@solana/buffer-layout'

export const STAKE_INSTRUCTIONS = {
Stake11111111111111111111111111111111111111: {
0: {
name: 'Stake Program - Initialize',
accounts: [{ name: 'Stake Account' }],
getDataUI: async (
_connection: Connection,
_data: Uint8Array,
_accounts: AccountMetaData[]
) => {
const layout = BufferLayout.struct<any['Initialize']>([
BufferLayout.u32('type'),
BufferLayout.struct(
[
BufferLayout.blob(32, 'staker'),
BufferLayout.blob(32, 'withdrawer'),
],
'authorized'
),
])

const decodedLayout = layout.decode(Buffer.from(_data))

return (
<>
<p>
Staker Authority:{' '}
{new PublicKey(decodedLayout.authorized.staker).toString()}
</p>
<p>
Withdrawer Authority:{' '}
{new PublicKey(decodedLayout.authorized.withdrawer).toString()}
</p>
</>
)
},
},
3: {
name: 'Stake Program - Split',
accounts: [],
Expand Down
27 changes: 17 additions & 10 deletions components/instructions/tools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { ConnectionContext } from '@utils/connection'
import { NFT_VOTER_INSTRUCTIONS } from './programs/nftVotingClient'
import { FORESIGHT_INSTRUCTIONS } from './programs/foresight'
import { LIDO_INSTRUCTIONS } from './programs/lido'
import { MEMO_INSTRUCTIONS } from './programs/memo'
import { NAME_SERVICE_INSTRUCTIONS } from './programs/nameService'
import { TOKEN_AUCTION_INSTRUCTIONS } from './programs/tokenAuction'
import { VALIDATORDAO_INSTRUCTIONS } from './programs/validatordao'
Expand Down Expand Up @@ -304,11 +305,16 @@ export const ACCOUNT_NAMES = {
'8XU6iRnVGp1DSWsbXWQVG3BofKncULJPEcU6YV6VRXDv': 'AllDomains Council Mint',
Hq1ffpMA4368gerKRAdVy7KFrUUMo2NwGwVwcXoFy1Th: 'AllDomains Community Rewards',
rP3eHs6uEDhQLqJHPLAwaNVENRezAgSnZK6opUtjhhT: 'AllDomains Grants',
'27Ma5zSVb8Sv9fuSZcXH2ZghTzdDXuWtzST4NJjXKKVo': 'AllDomains Rewards Governance',
'82s94bsTpcXfYbP7vTSwFfoi4cJEkoeQTfMif1h9s1AU': 'AllDomains Community Governance',
'CnixsSAVZqvaJEdkFHXXRQmot7RCSJFRHYMJvupbPoiE': 'AllDomains Foundation Governance 1',
'95vv4h7GWeBG7DbnzMwB15ZinFKBUiPeg6ea7ZqdGjZx': 'AllDomains Foundation Governance 2',
'6gwjRFcW1Y9iuJwXPdz1zZUa3Hcu855dH6APA5LjD8qK': 'AllDomains Treasury Governance',
'27Ma5zSVb8Sv9fuSZcXH2ZghTzdDXuWtzST4NJjXKKVo':
'AllDomains Rewards Governance',
'82s94bsTpcXfYbP7vTSwFfoi4cJEkoeQTfMif1h9s1AU':
'AllDomains Community Governance',
CnixsSAVZqvaJEdkFHXXRQmot7RCSJFRHYMJvupbPoiE:
'AllDomains Foundation Governance 1',
'95vv4h7GWeBG7DbnzMwB15ZinFKBUiPeg6ea7ZqdGjZx':
'AllDomains Foundation Governance 2',
'6gwjRFcW1Y9iuJwXPdz1zZUa3Hcu855dH6APA5LjD8qK':
'AllDomains Treasury Governance',
AWVUWfRnHCTgo123mRXB9BRWaxt6JdZXXKhFMQ5mryKJ: 'AllDomains DAO Governance',
}

Expand Down Expand Up @@ -435,6 +441,7 @@ export const INSTRUCTION_DESCRIPTORS = {
...MANGO_V4_INSTRUCTIONS,
...DUAL_INSTRUCTIONS,
...STAKE_INSTRUCTIONS,
...MEMO_INSTRUCTIONS,
}

export async function getInstructionDescriptor(
Expand All @@ -458,11 +465,11 @@ export async function getInstructionDescriptor(
const descriptor = !instruction.data.length
? descriptors
: descriptors && descriptors[instruction.data[0]]
? descriptors[instruction.data[0]]
: //backup if first number is same for couple of instructions inside same idl
descriptors && descriptors[`${instruction.data[0]}${instruction.data[1]}`]
? descriptors[`${instruction.data[0]}${instruction.data[1]}`]
: descriptors
? descriptors[instruction.data[0]]
: //backup if first number is same for couple of instructions inside same idl
descriptors && descriptors[`${instruction.data[0]}${instruction.data[1]}`]
? descriptors[`${instruction.data[0]}${instruction.data[1]}`]
: descriptors

const dataUI = (descriptor?.getDataUI &&
(await descriptor?.getDataUI(
Expand Down
10 changes: 9 additions & 1 deletion components/treasuryV2/Details/StakeDetails/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import cx from 'classnames'
import { Stake } from '@models/treasury/Asset'
import Address from '@components/Address'
import { DesktopComputerIcon } from '@heroicons/react/solid'
import { MARINADE_NATIVE_STAKING_AUTHORITY } from '@utils/marinade-native'

interface Props {
className?: string
Expand Down Expand Up @@ -59,7 +60,14 @@ export default function Header(props: Props) {
{props.account.amount}
</div>
</div>
<Address address={props.account.pubkey} className="text-xs" />
{props.account.raw.extensions.stake?.stakingAuthority.toString() ===
MARINADE_NATIVE_STAKING_AUTHORITY.toString() ? (
<span className="text-xs text-white/50">
Marinade Native Stake Accounts
</span>
) : (
<Address address={props.account.pubkey} className="text-xs" />
)}
</div>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions components/treasuryV2/Details/TokenDetails/Investments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Status } from '@utils/uiTypes/Result'
import { StrategyCard } from '@components/TreasuryAccount/AccountOverview'
import DepositModal from 'Strategies/components/DepositModal'
import Modal from '@components/Modal'
import ConvertToMsol from '@components/TreasuryAccount/ConvertToMsol'
import MarinadeModal from '@components/TreasuryAccount/MarinadeModal'
import ConvertToStSol from '@components/TreasuryAccount/ConvertToStSol'
import Trade from '@components/TreasuryAccount/Trade'
import useTreasuryAccountStore from 'stores/useTreasuryAccountStore'
Expand Down Expand Up @@ -210,7 +210,7 @@ export default function Investments(props: Props) {
sizeClassName="sm:max-w-3xl"
onClose={() => setAlternativeInvestment(null)}
>
<ConvertToMsol />
<MarinadeModal />
</Modal>
)}
{alternativeInvestment === 'Lido' && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import ProgramsListItem from './ProgramsListItem'
import UnknownAssetListItem from './UnknownAssetListItem'
import RealmAuthorityListItem from './RealmAuthorityListItem'
import StakeListItem from './StakeListItem'
import { abbreviateAddress } from '@utils/formatting'

interface Props {
className?: string
Expand Down Expand Up @@ -99,12 +98,10 @@ export default function OtherAssetsList(props: Props) {
<StakeListItem
key={i}
amount={asset.amount}
publicKey={
asset.raw.extensions.stake?.stakeAccount &&
abbreviateAddress(asset.raw.extensions.stake.stakeAccount!)
}
publicKey={asset.raw.extensions.stake?.stakeAccount.toString()}
stakingAuthority={asset.raw.extensions.stake?.stakingAuthority.toString()}
onSelect={() => props.onSelect?.(asset)}
></StakeListItem>
/>
)
case AssetType.Unknown:
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import { abbreviateAddress } from '@utils/formatting'
import { formatNumber } from '@utils/formatNumber'
import tokenPriceService from '@utils/services/tokenPrice'
import { WSOL_MINT } from '@components/instructions/tools'
import { MARINADE_NATIVE_STAKING_AUTHORITY } from '@utils/marinade-native'

interface Props {
className?: string
selected?: boolean
amount: number
publicKey: string | undefined
stakingAuthority: string | undefined
onSelect?(): void
}

Expand All @@ -20,7 +22,11 @@ export default function StakeListItem(props: Props) {
return (
<ListItem
className={props.className}
name={`Stake Account - ${abbreviateAddress(props.publicKey!)}`}
name={
props.stakingAuthority === MARINADE_NATIVE_STAKING_AUTHORITY.toString()
? 'Marinade Native Stake Accounts'
: `Stake Account - ${abbreviateAddress(props.publicKey!)}`
}
rhs={
<div className="flex items-end flex-col">
<div className="flex items-center space-x-1">
Expand Down
3 changes: 1 addition & 2 deletions hooks/useAccountInvestments/staticInvestments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ export const getSolInvestments = () => [
handledTokenSymbol: '',
handledTokenImgSrc:
'https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png',
protocolLogoSrc:
'https://raw.githubusercontent.com/LP-Finance-Inc/token-image/main/msol.png',
protocolLogoSrc: '/realms/MNDE/img/mnde_logo.png',
strategyName: 'Stake',
strategyDescription: '',
createProposalFcn: () => null,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"@heroicons/react": "1.0.6",
"@hookform/resolvers": "2.8.10",
"@identity.com/sol-did-client": "3.1.4",
"@marinade.finance/marinade-ts-sdk": "2.0.9",
"@marinade.finance/marinade-ts-sdk": "5.0.5",
"@mean-dao/payment-streaming": "4.0.3",
"@metaplex-foundation/js": "0.19.4",
"@metaplex-foundation/mpl-token-metadata": "2.10.0",
Expand Down
Loading