Orca Typescript SDK のサンプル実装。トークンのSwap, Farm Deposit/Withdraw, Pool Deposit/Withdrawができる。
READMEのままで進められるが、一箇所だけyarnでmz(Modernize node.js to current ECMAScript standards)をインストールする部分があった。
Agenda
実装手順
1. ts-node
TypeScriptなので、事前にts-nodeが実行できるようにしておく。
2. パッケージインストール
フォルダと空のTSファイルを用意。
% mkdir orca-typescript-sdk
% cd orca-typescript-sdk
% touch index.ts
READMEのとおり、以下をインストールする。
% yarn add @orca-so/sdk @solana/web3.js decimal.js
mzが使われているため、それもインストールする。
% yarn add @types/mz
3. TS記述
READMEのUSAGEとDevnet Testingを参考に、作成した空のTSファイル「index.ts」に以下を記述する。(内容はREADMEの丸ごとコピペで、Devnet設定箇所も指定どおりに書き換え済み)
import { readFile } from "mz/fs";
import { Connection, Keypair } from "@solana/web3.js";
import { getOrca, OrcaFarmConfig, OrcaPoolConfig, Network } from "@orca-so/sdk";
import Decimal from "decimal.js";
const main = async () => {
/*** Setup ***/
// 1. Read secret key file to get owner keypair
const secretKeyString = await readFile("/Users/scuba/my-wallet/my-keypair.json", {
encoding: "utf8",
});
const secretKey = Uint8Array.from(JSON.parse(secretKeyString));
const owner = Keypair.fromSecretKey(secretKey);
// 2. Initialzie Orca object with mainnet connection
const connection = new Connection("https://api.devnet.solana.com", "singleGossip");
const orca = getOrca(connection, Network.DEVNET);
try {
/*** Swap ***/
// 3. We will be swapping 0.1 SOL for some ORCA
const orcaSolPool = orca.getPool(OrcaPoolConfig.ORCA_SOL);
const solToken = orcaSolPool.getTokenB();
const solAmount = new Decimal(0.1);
const quote = await orcaSolPool.getQuote(solToken, solAmount);
const orcaAmount = quote.getMinOutputAmount();
console.log(`Swap ${solAmount.toString()} SOL for at least ${orcaAmount.toNumber()} ORCA`);
const swapPayload = await orcaSolPool.swap(owner, solToken, solAmount, orcaAmount);
const swapTxId = await swapPayload.execute();
console.log("Swapped:", swapTxId, "\n");
/*** Pool Deposit ***/
// 4. Deposit SOL and ORCA for LP token
const { maxTokenAIn, maxTokenBIn, minPoolTokenAmountOut } = await orcaSolPool.getDepositQuote(
orcaAmount,
solAmount
);
console.log(
`Deposit at most ${maxTokenBIn.toNumber()} SOL and ${maxTokenAIn.toNumber()} ORCA, for at least ${minPoolTokenAmountOut.toNumber()} LP tokens`
);
const poolDepositPayload = await orcaSolPool.deposit(
owner,
maxTokenAIn,
maxTokenBIn,
minPoolTokenAmountOut
);
const poolDepositTxId = await poolDepositPayload.execute();
console.log("Pool deposited:", poolDepositTxId, "\n");
/*** Farm Deposit ***/
// 5. Deposit some ORCA_SOL LP token for farm token
const lpBalance = await orcaSolPool.getLPBalance(owner.publicKey);
const orcaSolFarm = orca.getFarm(OrcaFarmConfig.ORCA_SOL_AQ);
const farmDepositPayload = await orcaSolFarm.deposit(owner, lpBalance);
const farmDepositTxId = await farmDepositPayload.execute();
console.log("Farm deposited:", farmDepositTxId, "\n");
// Note 1: for double dip, repeat step 5 but with the double dip farm
// Note 2: to harvest reward, orcaSolFarm.harvest(owner)
// Note 3: to get harvestable reward amount, orcaSolFarm.getHarvestableAmount(owner.publicKey)
/*** Farm Withdraw ***/
// 6. Withdraw ORCA_SOL LP token, in exchange for farm token
const farmBalance = await orcaSolFarm.getFarmBalance(owner.publicKey); // withdraw the entire balance
const farmWithdrawPayload = await orcaSolFarm.withdraw(owner, farmBalance);
const farmWithdrawTxId = await farmWithdrawPayload.execute();
console.log("Farm withdrawn:", farmWithdrawTxId, "\n");
/*** Pool Withdraw ***/
// 6. Withdraw SOL and ORCA, in exchange for ORCA_SOL LP token
const withdrawTokenAmount = await orcaSolPool.getLPBalance(owner.publicKey);
const withdrawTokenMint = orcaSolPool.getPoolTokenMint();
const { maxPoolTokenAmountIn, minTokenAOut, minTokenBOut } = await orcaSolPool.getWithdrawQuote(
withdrawTokenAmount,
withdrawTokenMint
);
console.log(
`Withdraw at most ${maxPoolTokenAmountIn.toNumber()} ORCA_SOL LP token for at least ${minTokenAOut.toNumber()} ORCA and ${minTokenBOut.toNumber()} SOL`
);
const poolWithdrawPayload = await orcaSolPool.withdraw(
owner,
maxPoolTokenAmountIn,
minTokenAOut,
minTokenBOut
);
const poolWithdrawTxId = await poolWithdrawPayload.execute();
console.log("Pool withdrawn:", poolWithdrawTxId, "\n");
} catch (err) {
console.warn(err);
}
};
main()
.then(() => {
console.log("Done");
})
.catch((e) => {
console.error(e);
});
ただし、このままだとKeypairのパスがおかしいため、solana config getで正しい情報を取得して書き直す必要あり。
% solana config get
Config File: /Users/user/.config/solana/cli/config.yml
RPC URL: http://127.0.0.1:8899
WebSocket URL: ws://127.0.0.1:8900/ (computed)
Keypair Path: /Users/user/.config/solana/id.json
Commitment: confirmed
上記のKeypair Pathに書き換える。なお、チルダは使えなかった。
(こういう指定はできない → ~/.config/solana/id.json)
const secretKeyString = await readFile("/Users/user/.config/solana/id.json", {
4. 実行
% ts-node index.ts
Swap 0.1 SOL for at least 0.011221 ORCA
Swapped: 3PMMF1wBiLZejcokG9Udj7HfSP1qttb5CWMo7aWYbzUWtfq6sZdTBwzStZ1J2YGyzuoL3NKV5VjYiibEUD6RJ4e
Deposit at most 0.1 SOL and 0.011221 ORCA, for at least 1.021566 LP tokens
Pool deposited: w8iS1Fz8QNxA4p7aQsV4x86NyRXqTxy1XvXA1ciipJp4hDJR1yJyfgT9UydngSSWee23zGGo2gBHq78fEqk3LPY
Farm deposited: TkGhzcYwCTdvvyC76FkX9SZP8fvAqigujPvFVbTBEXj2Yr1qyAvNN8aTyUc8adD1yeV5LATa2tuxibdXQT6ohqD
Farm withdrawn: 5n4XsMfTa3CD9RTjT4WcoUs7VsHJds1Um3bZwTQzKuPxD8wnKACCipz4SjogPWp5R7DvTv4MMd7ZZJjr32YBcBug
Withdraw at most 1.021566 ORCA_SOL LP token for at least 0.011198 ORCA and 0.099433553 SOL
Pool withdrawn: 2hSDz5zhkpWYhRWkXtUEvk1isNvtDKbDNXiMgRAGmkW4CpEoUuMojqyCTf4awEo5YGhrGf6cmdsRMvQFVN92drQ2
Done
エラー対応
mzエラー
mzが入っていないため発生するエラー。
% ts-node index.ts
/usr/local/lib/node_modules/ts-node/src/index.ts:750
return new TSError(diagnosticText, diagnosticCodes);
^
TSError: ⨯ Unable to compile TypeScript:
index.ts:1:26 - error TS7016: Could not find a declaration file for module 'mz/fs'. '/Users/user/Desktop/blockchain/orca-typescript-sdk/node_modules/mz/fs.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/mz` if it exists or add a new declaration (.d.ts) file containing `declare module 'mz/fs';`
1 import { readFile } from "mz/fs";
~~~~~~~
at createTSError (/usr/local/lib/node_modules/ts-node/src/index.ts:750:12)
at reportTSError (/usr/local/lib/node_modules/ts-node/src/index.ts:754:19)
at getOutput (/usr/local/lib/node_modules/ts-node/src/index.ts:941:36)
at Object.compile (/usr/local/lib/node_modules/ts-node/src/index.ts:1243:30)
at Module.m._compile (/usr/local/lib/node_modules/ts-node/src/index.ts:1370:30)
at Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
at Object.require.extensions.<computed> [as .ts] (/usr/local/lib/node_modules/ts-node/src/index.ts:1374:12)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) {
diagnosticText: "\x1B[96mindex.ts\x1B[0m:\x1B[93m1\x1B[0m:\x1B[93m26\x1B[0m - \x1B[91merror\x1B[0m\x1B[90m TS7016: \x1B[0mCould not find a declaration file for module 'mz/fs'. '/Users/user/Desktop/blockchain/orca-typescript-sdk/node_modules/mz/fs.js' implicitly has an 'any' type.\n" +
" Try `npm i --save-dev @types/mz` if it exists or add a new declaration (.d.ts) file containing `declare module 'mz/fs';`\n" +
'\n' +
'\x1B[7m1\x1B[0m import { readFile } from "mz/fs";\n' +
'\x1B[7m \x1B[0m \x1B[91m ~~~~~~~\x1B[0m\n',
diagnosticCodes: [ 7016 ]
}
mzをインストールする。
% yarn add @types/mz
Keypairパスエラー
Keypairパスが見つからないというエラー。solana config getで正しいKeypairパスをTSファイルに記述する。
% ts-node index.ts
[Error: ENOENT: no such file or directory, open '/Users/scuba/my-wallet/my-keypair.json'] {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/Users/scuba/my-wallet/my-keypair.json'
}