Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
],
"dependencies": {
"@dappio-wonderland/gateway-idls": "^0.2.6",
"@dappio-wonderland/navigator": "^0.2.11",
"@dappio-wonderland/navigator": "^0.2.14",
"@jup-ag/core": "1.0.0-beta.26",
"@project-serum/anchor": "^0.24.2",
"@project-serum/borsh": "^0.2.5",
Expand Down
32 changes: 32 additions & 0 deletions src/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
nftFinance,
katana,
friktion,
lido,
} from "@dappio-wonderland/navigator";
import {
ActionType,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -109,6 +111,7 @@ export class GatewayBuilder {
// Extra Metadata
poolDirection: PoolDirection.Obverse,
swapMinOutAmount: new anchor.BN(0),
validatorIndex: 0,
};

this._metadata = {
Expand Down Expand Up @@ -1188,6 +1191,19 @@ export class GatewayBuilder {
);

break;
case SupportedProtocols.Lido:
this._metadata.vault = await lido.infos.getVault(
Copy link
Contributor

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 validatorIndex in GatewayState:

  1. Add validatorIndex as an optional field in DepositParams
  2. Add validatorIndex field (metadata section) in GatewayParams (this is a work-around)
  3. Update gatewayParams: this.params.validatorIndex = depositParams.validatorIndex

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");
}
Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add some descriptive comment here to explain the intention? Since this is a temporary work-around


protocol = new ProtocolLido(
this._provider.connection,
this._program,
await this.getGatewayStateKey(),
this.params
);

break;

default:
throw new Error("Unsupported Protocol");
}
Expand Down
58 changes: 16 additions & 42 deletions src/ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,34 @@ import { PublicKey } from "@solana/web3.js";

// Program IDs

export const GATEWAY_PROGRAM_ID = new PublicKey(
"GATEp6AEtXtwHABNWHKH9qeh3uJDZtZJ7YBNYzHsX3FS"
);
export const GATEWAY_PROGRAM_ID = new PublicKey("GATEp6AEtXtwHABNWHKH9qeh3uJDZtZJ7YBNYzHsX3FS");

export const RAYDIUM_ADAPTER_PROGRAM_ID = new PublicKey(
"ADPT1q4xG8F9m64cQyjqGe11cCXQq6vL4beY5hJavhQ5"
);
export const RAYDIUM_ADAPTER_PROGRAM_ID = new PublicKey("ADPT1q4xG8F9m64cQyjqGe11cCXQq6vL4beY5hJavhQ5");

export const SABER_ADAPTER_PROGRAM_ID = new PublicKey(
"ADPT4GbWTs9DXxo91YGBjNntYwLpXxn4gEbxfnUPfQoB"
);
export const SABER_ADAPTER_PROGRAM_ID = new PublicKey("ADPT4GbWTs9DXxo91YGBjNntYwLpXxn4gEbxfnUPfQoB");

export const TULIP_ADAPTER_PROGRAM_ID = new PublicKey(
"ADPT9nhC1asRcEB13FKymLTatqWGCuZHDznGgnakWKxW"
);
export const TULIP_ADAPTER_PROGRAM_ID = new PublicKey("ADPT9nhC1asRcEB13FKymLTatqWGCuZHDznGgnakWKxW");

export const SOLEND_ADAPTER_PROGRAM_ID = new PublicKey(
"ADPTCXAFfJFVqcw73B4PWRZQjMNo7Q3Yj4g7p4zTiZnQ"
);
export const SOLEND_ADAPTER_PROGRAM_ID = new PublicKey("ADPTCXAFfJFVqcw73B4PWRZQjMNo7Q3Yj4g7p4zTiZnQ");

export const NFT_FINANCE_ADAPTER_PROGRAM_ID = new PublicKey(
"ADPTyBr92sBCE1hdYBRvXbMpF4hKs17xyDjFPxopcsrh"
);
export const NFT_FINANCE_ADAPTER_PROGRAM_ID = new PublicKey("ADPTyBr92sBCE1hdYBRvXbMpF4hKs17xyDjFPxopcsrh");

export const SERUM_PROGRAM_ID = new PublicKey(
"9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
);
export const SERUM_PROGRAM_ID = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin");

export const NATIVE_SOL = new PublicKey("11111111111111111111111111111111");

export const WSOL = new PublicKey(
"So11111111111111111111111111111111111111112"
);
export const WSOL = new PublicKey("So11111111111111111111111111111111111111112");

export const LARIX_ADAPTER_PROGRAM_ID = new PublicKey(
"ADPTLQQ1Bwgybb2qge7QKSW7woDrhEjcLWG642qP2X4"
);
export const LARIX_ADAPTER_PROGRAM_ID = new PublicKey("ADPTLQQ1Bwgybb2qge7QKSW7woDrhEjcLWG642qP2X4");

export const LIFINITY_ADAPTER_PROGRAM_ID = new PublicKey(
"ADPTF4WmNPebELw6UvnSVBdL7BAqs5ceg9tyrHsQfrJK"
);
export const LIFINITY_ADAPTER_PROGRAM_ID = new PublicKey("ADPTF4WmNPebELw6UvnSVBdL7BAqs5ceg9tyrHsQfrJK");

export const ORCA_ADAPTER_PROGRAM_ID = new PublicKey(
"ADPTTyNqameXftbqsxwXhbs7v7XP8E82YMaUStPgjmU5"
);
export const ORCA_ADAPTER_PROGRAM_ID = new PublicKey("ADPTTyNqameXftbqsxwXhbs7v7XP8E82YMaUStPgjmU5");

export const KATANA_ADAPTER_PROGRAM_ID = new PublicKey(
"ADPTwDKJTizC3V8gZXDxt5uLjJv4pBnh1nTTf9dZJnS2"
);
export const KATANA_ADAPTER_PROGRAM_ID = new PublicKey("ADPTwDKJTizC3V8gZXDxt5uLjJv4pBnh1nTTf9dZJnS2");

export const FRANCIUM_ADAPTER_PROGRAM_ID = new PublicKey(
"ADPTax5HwQ2ZWVLmceCek8UrqMhwCy5q3SHwi8W71Kv2"
);
export const FRANCIUM_ADAPTER_PROGRAM_ID = new PublicKey("ADPTax5HwQ2ZWVLmceCek8UrqMhwCy5q3SHwi8W71Kv2");

export const FRIKTION_ADAPTER_PROGRAM_ID = new PublicKey(
"ADPTzbsaBdXA3FqXoPHjaTjPfh9kadxxFKxonZihP1Ji"
);
export const FRIKTION_ADAPTER_PROGRAM_ID = new PublicKey("ADPTzbsaBdXA3FqXoPHjaTjPfh9kadxxFKxonZihP1Ji");

export const LIDO_ADAPTER_PROGRAM_ID = new PublicKey("ADPTPxbHbEBo9A8E53P2PZnmw3ZYJuwc8ArQQkbJtqhx");
152 changes: 152 additions & 0 deletions src/protocols/lido.ts
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) =>
Copy link

@ayushpaharia ayushpaharia Dec 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move the heaviest validator logic over to DappioWonderland/navigator
in the Vault info wrapper itself

value.pubkey.equals(heaviestValidator.pubkey)
);

inputLayout.encode(
{
amount: new anchor.BN(params.withdrawAmount),
// Set validator index.
validatorIndex: heaviestValidatorIndex,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validatorIndex should be retrieved from params?

},
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 };
}
}
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ export enum SupportedProtocols {
Francium = 9,
Friktion = 10,
Katana = 11,
Lido = 12,
}

export interface RouteInfoExtend extends RouteInfo {
Expand Down Expand Up @@ -354,6 +355,7 @@ export interface WithdrawParams {
vaultId: anchor.web3.PublicKey;
withdrawAmount: number;
version?: number;
validatorIndex?: number;
}

export interface LockNFTParams {
Expand Down Expand Up @@ -458,6 +460,10 @@ export type GatewayParams = TypeDef<
{
name: "poolDirection";
type: "u8";
},
{
name: "validatorIndex";
type: "u32";
}
];
};
Expand Down
Loading