-
Notifications
You must be signed in to change notification settings - Fork 3
feat: Add lido protocol #22
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
base: master
Are you sure you want to change the base?
Changes from 18 commits
a923645
1825623
b66d18a
83f9246
349c8fc
72cbd18
7ad028f
5a6d789
859bd90
c877549
ab682f0
8c52d8d
8287e0c
5ec577a
725c7b5
595d563
f06d835
7eb7dfd
5f73732
5f82a78
94e8146
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ import { | |
| nftFinance, | ||
| katana, | ||
| friktion, | ||
| lido, | ||
| } from "@dappio-wonderland/navigator"; | ||
| import { | ||
| ActionType, | ||
|
|
@@ -63,6 +64,7 @@ import { ProtocolFrancium } from "./protocols/francium"; | |
| import { ProtocolKatana } from "./protocols/katana"; | ||
| import { ProtocolTulip } from "./protocols/tulip"; | ||
| import { ProtocolFriktion } from "./protocols/friktion"; | ||
| import { ProtocolLido } from "./protocols/lido"; | ||
|
|
||
| export class GatewayBuilder { | ||
| public params: GatewayParams; | ||
|
|
@@ -109,6 +111,7 @@ export class GatewayBuilder { | |
| // Extra Metadata | ||
| poolDirection: PoolDirection.Obverse, | ||
| swapMinOutAmount: new anchor.BN(0), | ||
| validatorIndex: 0, | ||
| }; | ||
|
|
||
| this._metadata = { | ||
|
|
@@ -1188,6 +1191,19 @@ export class GatewayBuilder { | |
| ); | ||
|
|
||
| break; | ||
| case SupportedProtocols.Lido: | ||
| this._metadata.vault = await lido.infos.getVault( | ||
| this._provider.connection, | ||
| depositParams.vaultId | ||
| ); | ||
| protocol = new ProtocolLido( | ||
| this._provider.connection, | ||
| this._program, | ||
| await this.getGatewayStateKey(), | ||
| this.params | ||
| ); | ||
|
|
||
| break; | ||
| default: | ||
| throw new Error("Unsupported Protocol"); | ||
| } | ||
|
|
@@ -1297,6 +1313,22 @@ export class GatewayBuilder { | |
|
|
||
| break; | ||
|
|
||
| case SupportedProtocols.Lido: | ||
| this._metadata.vault = await lido.infos.getVault( | ||
| this._provider.connection, | ||
| withdrawParams.vaultId | ||
| ); | ||
| this.params.validatorIndex = withdrawParams.validatorIndex; | ||
|
||
|
|
||
| protocol = new ProtocolLido( | ||
| this._provider.connection, | ||
| this._program, | ||
| await this.getGatewayStateKey(), | ||
| this.params | ||
| ); | ||
|
|
||
| break; | ||
|
|
||
| default: | ||
| throw new Error("Unsupported Protocol"); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| import * as anchor from "@project-serum/anchor"; | ||
| import { TOKEN_PROGRAM_ID, getAssociatedTokenAddress } from "@solana/spl-token-v2"; | ||
| import { DepositParams, GatewayParams, IProtocolVault, PAYLOAD_SIZE, WithdrawParams } from "../types"; | ||
| import { Gateway } from "@dappio-wonderland/gateway-idls"; | ||
| import { IVaultInfo, lido } from "@dappio-wonderland/navigator"; | ||
| import { struct, nu64, u8, u32 } from "buffer-layout"; | ||
| import { getActivityIndex, sigHash, createATAWithoutCheckIx, getGatewayAuthority } from "../utils"; | ||
| import { LIDO_ADAPTER_PROGRAM_ID } from "../ids"; | ||
|
|
||
| export class ProtocolLido implements IProtocolVault { | ||
| constructor( | ||
| private _connection: anchor.web3.Connection, | ||
| private _gatewayProgram: anchor.Program<Gateway>, | ||
| private _gatewayStateKey: anchor.web3.PublicKey, | ||
| private _gatewayParams: GatewayParams | ||
| ) {} | ||
| async deposit( | ||
| params: DepositParams, | ||
| vault: IVaultInfo, | ||
| userKey: anchor.web3.PublicKey | ||
| ): Promise<{ txs: anchor.web3.Transaction[]; input: Buffer }> { | ||
| // Handle payload input here | ||
| const inputLayout = struct([nu64("amount")]); | ||
| let payload = Buffer.alloc(PAYLOAD_SIZE); | ||
| inputLayout.encode( | ||
| { | ||
| amount: new anchor.BN(params.depositAmount), | ||
| }, | ||
| payload | ||
| ); | ||
|
|
||
| // Handle transaction here | ||
|
|
||
| let preInstructions = [] as anchor.web3.TransactionInstruction[]; | ||
|
|
||
| // Maybe better to do in navigator? | ||
| const bufferArray = [lido.LIDO_ADDRESS.toBuffer(), Buffer.from("reserve_account")]; | ||
| const [reserveAccount] = anchor.web3.PublicKey.findProgramAddressSync(bufferArray, lido.LIDO_PROGRAM_ID); | ||
| const bufferArrayMint = [lido.LIDO_ADDRESS.toBuffer(), Buffer.from("mint_authority")]; | ||
| const [mintAuthority] = anchor.web3.PublicKey.findProgramAddressSync(bufferArrayMint, lido.LIDO_PROGRAM_ID); | ||
|
|
||
| const recipientStSolAddress = await getAssociatedTokenAddress(vault.shareMint, userKey); | ||
|
|
||
| // TVerify if this requires a check here | ||
| preInstructions.push(await createATAWithoutCheckIx(userKey, vault.shareMint)); | ||
|
|
||
| const remainingAccounts = [ | ||
| { pubkey: lido.LIDO_ADDRESS, isSigner: false, isWritable: true }, // 0 | ||
| { pubkey: userKey, isSigner: true, isWritable: true }, // 1 wallet.publicKey | ||
| { pubkey: recipientStSolAddress, isSigner: false, isWritable: true }, // 2 | ||
| { pubkey: vault.shareMint, isSigner: false, isWritable: true }, // 3 | ||
| { pubkey: reserveAccount, isSigner: false, isWritable: true }, // 4 | ||
| { pubkey: mintAuthority, isSigner: false, isWritable: false }, // 5 | ||
| { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, // 6 | ||
| { pubkey: anchor.web3.SystemProgram.programId, isSigner: false, isWritable: false }, // 7 | ||
| ]; | ||
| const txDeposit = await this._gatewayProgram.methods | ||
| .deposit() | ||
| .accounts({ | ||
| gatewayState: this._gatewayStateKey, | ||
| adapterProgramId: LIDO_ADAPTER_PROGRAM_ID, | ||
| baseProgramId: lido.LIDO_PROGRAM_ID, | ||
| activityIndex: await getActivityIndex(userKey), | ||
| gatewayAuthority: getGatewayAuthority(), | ||
| }) | ||
| .preInstructions(preInstructions) | ||
| .remainingAccounts(remainingAccounts) | ||
| .transaction(); | ||
|
|
||
| return { txs: [txDeposit], input: payload }; | ||
| } | ||
|
|
||
| async withdraw( | ||
| params: WithdrawParams, | ||
| vault: IVaultInfo, | ||
| userKey: anchor.web3.PublicKey | ||
| ): Promise<{ txs: anchor.web3.Transaction[]; input: Buffer }> { | ||
| // Handle payload input here | ||
| const inputLayout = struct([nu64("amount"), u32("validatorIndex")]); | ||
| let payload = Buffer.alloc(PAYLOAD_SIZE); | ||
|
|
||
| const vaultInfo = vault as lido.VaultInfo; | ||
| const vaultInfoWrapper = new lido.VaultInfoWrapper(vaultInfo); | ||
|
|
||
| const heaviestValidator = vaultInfoWrapper.getHeaviestValidator(); | ||
|
|
||
| const heaviestValidatorIndex = vaultInfo.validators.entries.findIndex((value) => | ||
|
||
| value.pubkey.equals(heaviestValidator.pubkey) | ||
| ); | ||
|
|
||
| inputLayout.encode( | ||
| { | ||
| amount: new anchor.BN(params.withdrawAmount), | ||
| // Set validator index. | ||
| validatorIndex: heaviestValidatorIndex, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| }, | ||
| payload | ||
| ); | ||
|
|
||
| // Account to temporarily hold stSOL in | ||
| const receivingAccount = anchor.web3.Keypair.generate(); | ||
|
|
||
| const senderStSolAddress = await getAssociatedTokenAddress(vault.shareMint, userKey); | ||
|
|
||
| const bufferArrayStake = [lido.LIDO_ADDRESS.toBuffer(), Buffer.from("stake_authority")]; | ||
| const [stakeAuthority] = anchor.web3.PublicKey.findProgramAddressSync(bufferArrayStake, lido.LIDO_PROGRAM_ID); | ||
|
|
||
| const validatorStakeSeeds = [ | ||
| lido.LIDO_ADDRESS.toBuffer(), | ||
| heaviestValidator.pubkey.toBuffer(), | ||
| Buffer.from("validator_stake_account"), | ||
| Buffer.from(heaviestValidator.entry.stakeSeeds.begin.toArray("le", 8)), | ||
| ]; | ||
|
|
||
| const [validatorStakeAccount] = anchor.web3.PublicKey.findProgramAddressSync( | ||
| validatorStakeSeeds, | ||
| lido.LIDO_PROGRAM_ID | ||
| ); | ||
|
|
||
| // Set up accounts | ||
| const remainingAccounts = [ | ||
| { pubkey: lido.LIDO_ADDRESS, isSigner: false, isWritable: true }, // 1 | ||
| { pubkey: userKey, isSigner: true, isWritable: false }, // 2 | ||
| { pubkey: senderStSolAddress, isSigner: false, isWritable: true }, // 3 | ||
| { pubkey: vault.shareMint, isSigner: false, isWritable: true }, // 4 | ||
| { pubkey: heaviestValidator.pubkey, isSigner: false, isWritable: false }, // 5 | ||
| { pubkey: validatorStakeAccount, isSigner: false, isWritable: true }, // 6 | ||
| { pubkey: receivingAccount.publicKey, isSigner: true, isWritable: true }, // 7 | ||
| { pubkey: stakeAuthority, isSigner: false, isWritable: false }, // 8 | ||
| { pubkey: vaultInfo.validatorList, isSigner: false, isWritable: true }, // 9 | ||
| { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, // 10 | ||
| { pubkey: anchor.web3.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false }, // 11 | ||
| { pubkey: anchor.web3.SystemProgram.programId, isSigner: false, isWritable: false }, // 12 | ||
| { pubkey: anchor.web3.StakeProgram.programId, isSigner: false, isWritable: false }, // 13 | ||
| ]; | ||
|
|
||
| // Handle transaction here | ||
| const txWithdraw = await this._gatewayProgram.methods | ||
| .withdraw() | ||
| .accounts({ | ||
| gatewayState: this._gatewayStateKey, | ||
| adapterProgramId: LIDO_ADAPTER_PROGRAM_ID, | ||
| baseProgramId: lido.LIDO_PROGRAM_ID, | ||
| activityIndex: await getActivityIndex(userKey), | ||
| gatewayAuthority: getGatewayAuthority(), | ||
| }) | ||
| .remainingAccounts(remainingAccounts) | ||
| .transaction(); | ||
|
|
||
| return { txs: [txWithdraw], input: payload }; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in order to support
validatorIndexinGatewayState:validatorIndexas an optional field inDepositParamsvalidatorIndexfield (metadata section) inGatewayParams(this is a work-around)this.params.validatorIndex = depositParams.validatorIndex