Skip to content
This repository was archived by the owner on Jul 17, 2023. It is now read-only.

Commit db535ee

Browse files
author
scuba
authored
Aquafarm withdraw/deposit support (#41)
* Aquafarm withdraw/deposit support * ignore when user farm is not initialized * @orca-so/aquafarm version bump to ^0.0.12 * extract aquafarm as a separate api * comment todos * variable name changes
1 parent dc9b587 commit db535ee

File tree

17 files changed

+1258
-26
lines changed

17 files changed

+1258
-26
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"types": "dist/index.d.ts",
77
"license": "MIT",
88
"dependencies": {
9+
"@orca-so/aquafarm": "^0.0.12",
910
"@orca-so/stablecurve": "^1.0.5",
1011
"@solana/spl-token": "^0.1.5",
1112
"@solana/spl-token-swap": "^0.1.2",

src/constants/farms.ts

+391
Large diffs are not rendered by default.

src/constants/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from "./orca-defaults";
22
export * from "./pools";
3+
export * from "./farms";

src/model/orca-factory.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import { Connection } from "@solana/web3.js";
2-
import { OrcaPoolConfig, OrcaPool } from "..";
3-
import { orcaPoolConfigs } from "../constants/pools";
2+
import { OrcaPoolConfig, OrcaPool, OrcaFarmConfig, OrcaFarm } from "..";
3+
import { orcaPoolConfigs, orcaFarmConfigs } from "../constants";
4+
import { OrcaFarmImpl } from "./orca/farm/orca-farm";
45
import { OrcaPoolImpl } from "./orca/pool/orca-pool";
56

67
export class OrcaFactory {
78
getPool(connection: Connection, config: OrcaPoolConfig): OrcaPool {
89
return new OrcaPoolImpl(connection, orcaPoolConfigs[config]);
910
}
11+
12+
getFarm(connection: Connection, config: OrcaFarmConfig): OrcaFarm {
13+
return new OrcaFarmImpl(connection, orcaFarmConfigs[config]);
14+
}
1015
}

src/model/orca/farm/farm-types.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { PublicKey } from "@solana/web3.js";
2+
3+
/**
4+
* An Orca aquafarm token
5+
* @param address The farm account address
6+
* @param farmTokenMint Mint address for the farm token
7+
* @param rewardTokenMint Mint address for the farm reward token
8+
* @param baseTokenMint Mint address for the base token
9+
* @param baseTokenDecimals Number of decimal places for the base token
10+
*/
11+
export type OrcaFarmParams = {
12+
address: PublicKey;
13+
farmTokenMint: PublicKey;
14+
rewardTokenMint: PublicKey;
15+
baseTokenMint: PublicKey;
16+
baseTokenDecimals: number;
17+
};

src/model/orca/farm/orca-farm.ts

+227
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import { Aquafarm, fetchGlobalFarms, fetchUserFarms, getUserFarmAddress } from "@orca-so/aquafarm";
2+
import { TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
3+
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
4+
import Decimal from "decimal.js";
5+
import {
6+
deriveAssociatedTokenAddress,
7+
deserializeAccount,
8+
OrcaU64,
9+
ORCA_FARM_ID,
10+
resolveOrCreateAssociatedTokenAddress,
11+
TransactionBuilder,
12+
TransactionPayload,
13+
U64Utils,
14+
} from "../../..";
15+
import { OrcaFarm } from "../../../public/";
16+
import {
17+
createFarmConvertTokensInstruction,
18+
createFarmRevertTokensInstruction,
19+
createInitUserFarmInstruction,
20+
} from "../../../public/utils/web3/instructions/farm-instructions";
21+
import { createApprovalInstruction } from "../../../public/utils/web3/instructions/pool-instructions";
22+
import { Owner } from "../../../public/utils/web3/key-utils";
23+
import { OrcaFarmParams } from "./farm-types";
24+
25+
export class OrcaFarmImpl implements OrcaFarm {
26+
private connection: Connection;
27+
private farmParams: OrcaFarmParams;
28+
29+
constructor(connection: Connection, config: OrcaFarmParams) {
30+
this.connection = connection;
31+
this.farmParams = config;
32+
}
33+
34+
public async getFarmBalance(owner: PublicKey): Promise<OrcaU64> {
35+
const address = await deriveAssociatedTokenAddress(owner, this.farmParams.farmTokenMint);
36+
37+
const accountInfo = await this.connection.getAccountInfo(address);
38+
39+
// User does not have a balance for this account
40+
if (accountInfo == undefined) {
41+
return OrcaU64.fromNumber(0, this.farmParams.baseTokenDecimals);
42+
}
43+
const result = deserializeAccount(accountInfo?.data);
44+
if (result == undefined) {
45+
throw new Error("Failed to parse user account for LP token.");
46+
}
47+
48+
return OrcaU64.fromU64(result.amount, this.farmParams.baseTokenDecimals);
49+
}
50+
51+
public async getFarmSupply(): Promise<OrcaU64> {
52+
const context = await this.connection.getTokenSupply(this.farmParams.farmTokenMint);
53+
54+
const amt = new u64(context.value.amount);
55+
56+
return OrcaU64.fromU64(amt, this.farmParams.baseTokenDecimals);
57+
}
58+
59+
public async deposit(
60+
owner: Keypair | PublicKey,
61+
baseTokenAmount: Decimal | OrcaU64
62+
): Promise<TransactionPayload> {
63+
const _owner = new Owner(owner);
64+
const ownerAddress = _owner.publicKey;
65+
66+
const baseTokenAmount_U64 = U64Utils.toFarmU64(
67+
baseTokenAmount,
68+
this.farmParams,
69+
"baseTokenAmount"
70+
);
71+
72+
const { address: farmAddress, rewardTokenMint } = this.farmParams;
73+
const userFarmPublicKey = (
74+
await getUserFarmAddress(farmAddress, ownerAddress, TOKEN_PROGRAM_ID, ORCA_FARM_ID)
75+
)[0];
76+
77+
const globalFarms = await fetchGlobalFarms(this.connection, [farmAddress], ORCA_FARM_ID);
78+
const userFarms = await fetchUserFarms(
79+
this.connection,
80+
ownerAddress,
81+
[farmAddress],
82+
ORCA_FARM_ID
83+
);
84+
85+
if (!globalFarms) {
86+
throw new Error("Failed to get globalFarms information");
87+
}
88+
const farm = new Aquafarm(globalFarms[0], ORCA_FARM_ID, userFarms && userFarms[0]);
89+
90+
// If the user lacks the user farm, create it
91+
const initUserFarmInstruction = await createInitUserFarmInstruction(
92+
farm,
93+
userFarmPublicKey,
94+
_owner
95+
);
96+
97+
// If the user lacks the farm token account, create it
98+
const { address: userFarmTokenPublicKey, ...resolveFarmTokenInstructions } =
99+
await resolveOrCreateAssociatedTokenAddress(
100+
this.connection,
101+
_owner,
102+
farm.globalFarm.farmTokenMint
103+
);
104+
105+
// If the user lacks the reward token account, create it
106+
const { address: userRewardTokenPublicKey, ...resolveRewardTokenInstructions } =
107+
await resolveOrCreateAssociatedTokenAddress(this.connection, _owner, rewardTokenMint);
108+
109+
// If the user lacks the base token account, create it
110+
const { address: userBaseTokenPublicKey, ...resolveBaseTokenInstructions } =
111+
await resolveOrCreateAssociatedTokenAddress(
112+
this.connection,
113+
_owner,
114+
this.farmParams.baseTokenMint
115+
);
116+
117+
// Approve transfer of base token to be converted to farm tokens
118+
const { userTransferAuthority, ...transferBaseTokenInstruction } = createApprovalInstruction(
119+
ownerAddress,
120+
baseTokenAmount_U64,
121+
userBaseTokenPublicKey
122+
);
123+
124+
// Convert base tokens to farm tokens
125+
const convertToFarmTokens = await createFarmConvertTokensInstruction(
126+
farm,
127+
userTransferAuthority.publicKey,
128+
userBaseTokenPublicKey,
129+
userFarmTokenPublicKey,
130+
userRewardTokenPublicKey,
131+
baseTokenAmount_U64,
132+
userFarmPublicKey,
133+
_owner
134+
);
135+
136+
return await new TransactionBuilder(this.connection, ownerAddress, _owner)
137+
.addInstruction(initUserFarmInstruction)
138+
.addInstruction(resolveFarmTokenInstructions)
139+
.addInstruction(resolveBaseTokenInstructions)
140+
.addInstruction(resolveRewardTokenInstructions)
141+
.addInstruction(transferBaseTokenInstruction)
142+
.addInstruction(convertToFarmTokens)
143+
.build();
144+
}
145+
146+
public async withdraw(
147+
owner: Keypair | PublicKey,
148+
baseTokenAmount: Decimal | OrcaU64
149+
): Promise<TransactionPayload> {
150+
const _owner = new Owner(owner);
151+
const ownerAddress = _owner.publicKey;
152+
153+
const baseTokenAmount_U64 = U64Utils.toFarmU64(
154+
baseTokenAmount,
155+
this.farmParams,
156+
"baseTokenAmount"
157+
);
158+
159+
const { address: farmAddress, rewardTokenMint } = this.farmParams;
160+
const userFarmPublicKey = (
161+
await getUserFarmAddress(farmAddress, ownerAddress, TOKEN_PROGRAM_ID, ORCA_FARM_ID)
162+
)[0];
163+
164+
const globalFarms = await fetchGlobalFarms(this.connection, [farmAddress], ORCA_FARM_ID);
165+
const userFarms = await fetchUserFarms(
166+
this.connection,
167+
ownerAddress,
168+
[farmAddress],
169+
ORCA_FARM_ID
170+
);
171+
172+
if (!globalFarms) {
173+
throw new Error("Failed to get globalFarms information");
174+
}
175+
const farm = new Aquafarm(globalFarms[0], ORCA_FARM_ID, userFarms && userFarms[0]);
176+
177+
if (!farm.isUserFarmInitialized()) {
178+
throw new Error("Failed to get userFarm information. Warning: withdraw from deposit address");
179+
}
180+
181+
// If the user lacks the farm token account, create it
182+
const { address: userFarmTokenPublicKey, ...resolveFarmTokenInstructions } =
183+
await resolveOrCreateAssociatedTokenAddress(
184+
this.connection,
185+
_owner,
186+
farm.globalFarm.farmTokenMint
187+
);
188+
189+
// If the user lacks the reward token account, create it
190+
const { address: userRewardTokenPublicKey, ...resolveRewardTokenInstructions } =
191+
await resolveOrCreateAssociatedTokenAddress(this.connection, _owner, rewardTokenMint);
192+
193+
// Get user's baseToken token account
194+
const { address: userBaseTokenPublicKey, ...resolveBaseTokenInstructions } =
195+
await resolveOrCreateAssociatedTokenAddress(
196+
this.connection,
197+
_owner,
198+
this.farmParams.baseTokenMint
199+
);
200+
201+
// Approve transfer of farm tokens to be reverted to base tokens
202+
const { userTransferAuthority, ...transferFarmTokenInstruction } = createApprovalInstruction(
203+
ownerAddress,
204+
baseTokenAmount_U64,
205+
userFarmTokenPublicKey
206+
);
207+
208+
// Revert farm tokens to base tokens
209+
const revertFromFarmTokens = await createFarmRevertTokensInstruction(
210+
farm,
211+
userTransferAuthority.publicKey,
212+
userBaseTokenPublicKey,
213+
userFarmTokenPublicKey,
214+
userRewardTokenPublicKey,
215+
baseTokenAmount_U64,
216+
_owner
217+
);
218+
219+
return await new TransactionBuilder(this.connection, ownerAddress, _owner)
220+
.addInstruction(resolveFarmTokenInstructions)
221+
.addInstruction(resolveRewardTokenInstructions)
222+
.addInstruction(resolveBaseTokenInstructions)
223+
.addInstruction(transferFarmTokenInstruction)
224+
.addInstruction(revertFromFarmTokens)
225+
.build();
226+
}
227+
}

src/model/orca/orca-impl.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Connection } from "@solana/web3.js";
2-
import { Orca, OrcaPool, OrcaPoolConfig } from "../../public";
2+
import { Orca, OrcaFarm, OrcaPool, OrcaPoolConfig, OrcaFarmConfig } from "../../public";
33
import { OrcaFactory } from "../orca-factory";
44

55
export class OrcaImpl implements Orca {
@@ -14,4 +14,8 @@ export class OrcaImpl implements Orca {
1414
getPool(pool: OrcaPoolConfig): OrcaPool {
1515
return this.factory.getPool(this.connection, pool);
1616
}
17+
18+
getFarm(farm: OrcaFarmConfig): OrcaFarm {
19+
return this.factory.getFarm(this.connection, farm);
20+
}
1721
}

src/model/orca/pool/orca-pool.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ export class OrcaPoolImpl implements OrcaPool {
287287
const poolTokenAmountIn_U64 = U64Utils.toPoolU64(
288288
poolTokenAmountIn,
289289
this.poolParams,
290-
"poolTokenAmount"
290+
"poolTokenAmountIn"
291291
);
292292

293293
// Create a token account for tokenA, if necessary

src/public/farms/config.ts

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* The following content is auto-generated.
3+
*/
4+
5+
/**
6+
* A list of supported Orca farms in this SDK.
7+
*/
8+
export enum OrcaFarmConfig {
9+
SOL_USDC_AQ = "APDFRM3HMr8CAGXwKHiu2f5ePSpaiEJhaURwhsRrUUt9",
10+
SOL_USDT_AQ = "FZthQCuYHhcfiDma7QrX7buDHwrZEd7vL8SjS6LQa3Tx",
11+
ETH_SOL_AQ = "71FymgN2ZUf7VvVTLE8jYEnjP3jSK1Frp2XT1nHs8Hob",
12+
ETH_USDC_AQ = "3e1W6Aqcbuk2DfHUwRiRcyzpyYRRjg6yhZZcyEARydUX",
13+
RAY_SOL_AQ = "5kimD5W6yJpHRHCyPtnEyDsQRdiiJKivu5AqN3si82Jc",
14+
ROPE_SOL_AQ = "ADrvfPBsRcJfGsN6Bs385zYddH52nuM5FA8UaAkX9o2V",
15+
STEP_SOL_AQ = "8nTzqDXHriG2CXKbybeuEh1EqDQMtrbYMFWcP7AkiDaP",
16+
SRM_SOL_AQ = "9tf8rBSEQYG7AqL896fN2nZi1iYPqpWaLEdpbeQaC1Vy",
17+
FTT_SOL_AQ = "EsYaDKJCmcJtJHFuJYwQZwqohvVMCrFzcg8yo3i328No",
18+
COPE_SOL_AQ = "CzieDbGRdN1QGaGDNpSqzEA18bi881ccvkkGZi51pe1k",
19+
OXY_SOL_AQ = "7tYCdLN84EnTMkxM7HNamWJx7F4xgKe9KiiWvLyWjbgT",
20+
BTC_SOL_AQ = "Acxs19v6eUMTEfdvkvWkRB4bwFCHm3XV9jABCy7c1mXe",
21+
MER_SOL_AQ = "HiwRobjfHZ4zsPtqCC4oBS24pSmy4t8GGkXRbQj4yU6L",
22+
FIDA_SOL_AQ = "EYsNdtyu4gGTaGz8N5m5iQ3G1N6rDyMbR72B3CqbWW4W",
23+
MAPS_SOL_AQ = "99pfC8fWymXgbq3CvrExhx3UxQDC1fMWEWLbNT83F45e",
24+
USDC_USDT_AQ = "H2uzgruPvonVpCRhwwdukcpXK8TG17swFNzYFr2rtPxy",
25+
ORCA_SOL_AQ = "2uVjAuRXavpM6h1scGQaxqb6HVaNRn6T2X7HHXTabz25",
26+
ORCA_USDC_AQ = "n8Mpu28RjeYD7oUX3LG1tPxzhRZh3YYLRSHcHRdS3Zx",
27+
KIN_SOL_AQ = "HEvnD66WcBfTajS9adUYnGRBMDehFtLySiFHSD6kEBWs",
28+
SAMO_SOL_AQ = "D6N9j8F2DhtzDtrdpT74y3u2YmYAzcggiLc3nTjqux9M",
29+
LIQ_USDC_AQ = "3PD9SZFwXKkXr4akLf4ofo37ZUMycwML89R2P3qxcbZG",
30+
SNY_USDC_AQ = "AZpo4BJHHRetF96v6SGinFZBMXM4yWMo4RA8C4PriDLk",
31+
mSOL_USDC_AQ = "8PSfyiTVwPb6Rr2iZ8F3kNpbg65BCfJM9v8LfB916r44",
32+
SLRS_USDC_AQ = "AtB4nUmdyQfuWWJ9xAHw9xyVnJFfSjSuVWkiYan8y86w",
33+
PORT_USDC_AQ = "F8gPSpwVHj8FdAJAYULDuZBxFEJut87hUbARYYx3471w",
34+
SBR_USDC_AQ = "CS7fA5n4c2D82dUoHrYzS3gAqgqaoVSfgsr18kitp2xo",
35+
SOCN_USDC_AQ = "Dkr8B675PGnNwEr9vTKXznjjHke5454EQdz3iaSbparB",
36+
pSOL_USDC_AQ = "C2YzN6MymD5HM2kPaH7bzcbqciyjfmpqyVaR3KA5V6z1",
37+
mSOL_SOL_AQ = "29cdoMgu6MS2VXpcMo1sqRdWEzdUR9tjvoh8fcK8Z87R",
38+
ORCA_PAI_AQ = "C7TH2jEJJaxVwwuvkbcDGfHUiZvEkkeYjyAcdTMi5ujb",
39+
ORCA_mSOL_AQ = "CVapmQn7HaU1yMDW3q6oUV4hx6XoYv54T4zfGXkuJqkA",
40+
SOCN_SOL_AQ = "APNpzQvR91v1THbsAyG3HHrUEwvexWYeNCFLQuVnxgMc",
41+
ATLAS_USDC_AQ = "FZ8x1LCRSPDeHBDoAc3Gc6Y7ETCynuHEr5q5YWV7uRCJ",
42+
POLIS_USDC_AQ = "GteBdo9sqE7T41G8AJsaG9WHW48uXBwsLLznmu2TBdgy",
43+
BOP_USDC_AQ = "2gXDJZ7XAtQEtf4PRSQZKoq1WMuu1H44tQanbMA3YVpu",
44+
SAMO_USDC_AQ = "6VK1ksrmYGMBWUUZfygGF8tHRGpNxQEWv8pfvzQHdyyc",
45+
NINJA_SOL_AQ = "4X1oYoFWYtLebk51zuh889r1WFLe8Z9qWApj87hQMfML",
46+
SLIM_USDC_AQ = "BVWwyiHVHZQMPHsiW7dZH7bnBVKmbxdeEjWqVRciHCyo",
47+
wHAPI_USDC_AQ = "ELfBngAgvLEHVBuJQhhE7AW6eqLX7id2sfrBngVNVAUW",
48+
COPE_USDC_AQ = "HsauTv9s52Zv12eaDuSp6y7BEm4e4BHEyAsbdjyyWzPK",
49+
SUNNY_USDC_AQ = "GHuoeq9UnFBsBhMwH43eL3RWX5XVXbSRYJymmyMYpT7n",
50+
GRAPE_USDC_AQ = "EorFh8siFyLF1QTZ7cCXQaPGqyo7eb4SAgKtRH8Jcxjd",
51+
}

src/public/farms/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./config";
2+
export * from "./types";

0 commit comments

Comments
 (0)