@orca-so/whirlpools-sdk
Advanced tools
Comparing version 0.5.1 to 0.5.2
@@ -12,2 +12,3 @@ export declare enum MathErrorCode { | ||
export declare enum SwapErrorCode { | ||
InvalidDevFeePercentage = "InvalidDevFeePercentage", | ||
InvalidSqrtPriceLimitDirection = "InvalidSqrtPriceLimitDirection", | ||
@@ -14,0 +15,0 @@ SqrtPriceOutOfBounds = "SqrtPriceOutOfBounds", |
@@ -18,2 +18,3 @@ "use strict"; | ||
(function (SwapErrorCode) { | ||
SwapErrorCode["InvalidDevFeePercentage"] = "InvalidDevFeePercentage"; | ||
SwapErrorCode["InvalidSqrtPriceLimitDirection"] = "InvalidSqrtPriceLimitDirection"; | ||
@@ -20,0 +21,0 @@ SwapErrorCode["SqrtPriceOutOfBounds"] = "SqrtPriceOutOfBounds"; |
import { Percentage, TransactionBuilder } from "@orca-so/common-sdk"; | ||
import { Address } from "@project-serum/anchor"; | ||
import { PublicKey } from "@solana/web3.js"; | ||
import { WhirlpoolContext } from "../context"; | ||
import { IncreaseLiquidityInput } from "../instructions"; | ||
import { DevFeeSwapInput, IncreaseLiquidityInput, SwapInput } from "../instructions"; | ||
import { AccountFetcher } from "../network/public"; | ||
import { TokenAccountInfo, TokenInfo, WhirlpoolData, WhirlpoolRewardInfo } from "../types/public"; | ||
import { Whirlpool } from "../whirlpool-client"; | ||
import { PublicKey } from "@solana/web3.js"; | ||
import { AccountFetcher } from "../network/public"; | ||
import { SwapQuote } from "../quotes/public"; | ||
export declare class WhirlpoolImpl implements Whirlpool { | ||
@@ -39,3 +38,4 @@ readonly ctx: WhirlpoolContext; | ||
closePosition(positionAddress: Address, slippageTolerance: Percentage, destinationWallet?: Address, positionWallet?: Address, payer?: Address): Promise<TransactionBuilder>; | ||
swap(quote: SwapQuote, sourceWallet?: Address): Promise<TransactionBuilder>; | ||
swap(quote: SwapInput, sourceWallet?: Address): Promise<TransactionBuilder>; | ||
swapWithDevFees(quote: DevFeeSwapInput, devFeeWallet: PublicKey, wallet?: PublicKey | undefined, payer?: PublicKey | undefined): Promise<TransactionBuilder>; | ||
/** | ||
@@ -42,0 +42,0 @@ * Construct a transaction for opening an new position with optional metadata |
@@ -29,7 +29,7 @@ "use strict"; | ||
const anchor_1 = require("@project-serum/anchor"); | ||
const instructions_1 = require("../instructions"); | ||
const web3_js_1 = require("@solana/web3.js"); | ||
const tiny_invariant_1 = __importDefault(require("tiny-invariant")); | ||
const public_1 = require("../utils/public"); | ||
const public_2 = require("../quotes/public"); | ||
const instructions_1 = require("../instructions"); | ||
const public_1 = require("../quotes/public"); | ||
const public_2 = require("../utils/public"); | ||
const util_1 = require("./util"); | ||
@@ -89,3 +89,3 @@ class WhirlpoolImpl { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const initTickArrayStartPdas = yield public_1.TickArrayUtil.getUninitializedArraysPDAs(ticks, this.ctx.program.programId, this.address, this.data.tickSpacing, this.fetcher, refresh); | ||
const initTickArrayStartPdas = yield public_2.TickArrayUtil.getUninitializedArraysPDAs(ticks, this.ctx.program.programId, this.address, this.data.tickSpacing, this.fetcher, refresh); | ||
if (!initTickArrayStartPdas.length) { | ||
@@ -127,2 +127,14 @@ return null; | ||
} | ||
swapWithDevFees(quote, devFeeWallet, wallet, payer) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const sourceWalletKey = wallet ? common_sdk_1.AddressUtil.toPubKey(wallet) : this.ctx.wallet.publicKey; | ||
const payerKey = payer ? common_sdk_1.AddressUtil.toPubKey(payer) : this.ctx.wallet.publicKey; | ||
const txBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet); | ||
if (!quote.devFeeAmount.eq(common_sdk_1.ZERO)) { | ||
const inputToken = quote.aToB === quote.amountSpecifiedIsInput ? this.getTokenAInfo() : this.getTokenBInfo(); | ||
txBuilder.addInstruction(yield common_sdk_1.TokenUtil.createSendTokensToWalletInstruction(this.ctx.connection, sourceWalletKey, devFeeWallet, inputToken.mint, inputToken.decimals, quote.devFeeAmount, () => this.ctx.fetcher.getAccountRentExempt(), payerKey)); | ||
} | ||
return this.getSwapTx(quote, sourceWalletKey, txBuilder); | ||
}); | ||
} | ||
/** | ||
@@ -133,4 +145,4 @@ * Construct a transaction for opening an new position with optional metadata | ||
return __awaiter(this, void 0, void 0, function* () { | ||
(0, tiny_invariant_1.default)(public_1.TickUtil.checkTickInBounds(tickLower), "tickLower is out of bounds."); | ||
(0, tiny_invariant_1.default)(public_1.TickUtil.checkTickInBounds(tickUpper), "tickUpper is out of bounds."); | ||
(0, tiny_invariant_1.default)(public_2.TickUtil.checkTickInBounds(tickLower), "tickLower is out of bounds."); | ||
(0, tiny_invariant_1.default)(public_2.TickUtil.checkTickInBounds(tickUpper), "tickUpper is out of bounds."); | ||
const { liquidityAmount: liquidity, tokenMaxA, tokenMaxB } = liquidityInput; | ||
@@ -142,7 +154,7 @@ (0, tiny_invariant_1.default)(liquidity.gt(new anchor_1.BN(0)), "liquidity must be greater than zero"); | ||
} | ||
(0, tiny_invariant_1.default)(public_1.TickUtil.isTickInitializable(tickLower, whirlpool.tickSpacing), `lower tick ${tickLower} is not an initializable tick for tick-spacing ${whirlpool.tickSpacing}`); | ||
(0, tiny_invariant_1.default)(public_1.TickUtil.isTickInitializable(tickUpper, whirlpool.tickSpacing), `upper tick ${tickUpper} is not an initializable tick for tick-spacing ${whirlpool.tickSpacing}`); | ||
(0, tiny_invariant_1.default)(public_2.TickUtil.isTickInitializable(tickLower, whirlpool.tickSpacing), `lower tick ${tickLower} is not an initializable tick for tick-spacing ${whirlpool.tickSpacing}`); | ||
(0, tiny_invariant_1.default)(public_2.TickUtil.isTickInitializable(tickUpper, whirlpool.tickSpacing), `upper tick ${tickUpper} is not an initializable tick for tick-spacing ${whirlpool.tickSpacing}`); | ||
const positionMintKeypair = web3_js_1.Keypair.generate(); | ||
const positionPda = public_1.PDAUtil.getPosition(this.ctx.program.programId, positionMintKeypair.publicKey); | ||
const metadataPda = public_1.PDAUtil.getPositionMetadata(positionMintKeypair.publicKey); | ||
const positionPda = public_2.PDAUtil.getPosition(this.ctx.program.programId, positionMintKeypair.publicKey); | ||
const metadataPda = public_2.PDAUtil.getPositionMetadata(positionMintKeypair.publicKey); | ||
const positionTokenAccountAddress = yield (0, common_sdk_1.deriveATA)(wallet, positionMintKeypair.publicKey); | ||
@@ -170,4 +182,4 @@ const txBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet); | ||
txBuilder.addInstruction(tokenOwnerAccountBIx); | ||
const tickArrayLowerPda = public_1.PDAUtil.getTickArrayFromTickIndex(tickLower, this.data.tickSpacing, this.address, this.ctx.program.programId); | ||
const tickArrayUpperPda = public_1.PDAUtil.getTickArrayFromTickIndex(tickUpper, this.data.tickSpacing, this.address, this.ctx.program.programId); | ||
const tickArrayLowerPda = public_2.PDAUtil.getTickArrayFromTickIndex(tickLower, this.data.tickSpacing, this.address, this.ctx.program.programId); | ||
const tickArrayUpperPda = public_2.PDAUtil.getTickArrayFromTickIndex(tickUpper, this.data.tickSpacing, this.address, this.ctx.program.programId); | ||
const liquidityIx = (0, instructions_1.increaseLiquidityIx)(this.ctx.program, { | ||
@@ -203,4 +215,4 @@ liquidityAmount: liquidity, | ||
(0, tiny_invariant_1.default)(position.whirlpool.equals(this.address), `Position ${positionAddress.toBase58()} is not a position for Whirlpool ${this.address.toBase58()}`); | ||
const tickArrayLower = public_1.PDAUtil.getTickArrayFromTickIndex(position.tickLowerIndex, whirlpool.tickSpacing, position.whirlpool, this.ctx.program.programId).publicKey; | ||
const tickArrayUpper = public_1.PDAUtil.getTickArrayFromTickIndex(position.tickUpperIndex, whirlpool.tickSpacing, position.whirlpool, this.ctx.program.programId).publicKey; | ||
const tickArrayLower = public_2.PDAUtil.getTickArrayFromTickIndex(position.tickLowerIndex, whirlpool.tickSpacing, position.whirlpool, this.ctx.program.programId).publicKey; | ||
const tickArrayUpper = public_2.PDAUtil.getTickArrayFromTickIndex(position.tickUpperIndex, whirlpool.tickSpacing, position.whirlpool, this.ctx.program.programId).publicKey; | ||
const positionTokenAccount = yield (0, common_sdk_1.deriveATA)(positionWallet, position.positionMint); | ||
@@ -225,3 +237,3 @@ const txBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet); | ||
if (position.liquidity.gt(new anchor_1.BN(0))) { | ||
const decreaseLiqQuote = (0, public_2.decreaseLiquidityQuoteByLiquidityWithParams)({ | ||
const decreaseLiqQuote = (0, public_1.decreaseLiquidityQuoteByLiquidityWithParams)({ | ||
liquidity: position.liquidity, | ||
@@ -263,7 +275,8 @@ slippageTolerance, | ||
} | ||
getSwapTx(input, wallet) { | ||
getSwapTx(input, wallet, initTxBuilder) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
(0, tiny_invariant_1.default)(input.amount.gt(common_sdk_1.ZERO), "swap amount must be more than zero."); | ||
const { amount, aToB } = input; | ||
const whirlpool = this.data; | ||
const txBuilder = new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet); | ||
const txBuilder = initTxBuilder !== null && initTxBuilder !== void 0 ? initTxBuilder : new common_sdk_1.TransactionBuilder(this.ctx.provider.connection, this.ctx.provider.wallet); | ||
const [ataA, ataB] = yield (0, common_sdk_1.resolveOrCreateATAs)(this.ctx.connection, wallet, [ | ||
@@ -277,3 +290,3 @@ { tokenMint: whirlpool.tokenMintA, wrappedSolAmountIn: aToB ? amount : common_sdk_1.ZERO }, | ||
txBuilder.addInstruction(tokenOwnerAccountBIx); | ||
const oraclePda = public_1.PDAUtil.getOracle(this.ctx.program.programId, this.address); | ||
const oraclePda = public_2.PDAUtil.getOracle(this.ctx.program.programId, this.address); | ||
txBuilder.addInstruction((0, instructions_1.swapIx)(this.ctx.program, Object.assign(Object.assign({}, input), { whirlpool: this.address, tokenAuthority: wallet, tokenOwnerAccountA, tokenVaultA: whirlpool.tokenVaultA, tokenOwnerAccountB, tokenVaultB: whirlpool.tokenVaultB, oracle: oraclePda.publicKey }))); | ||
@@ -280,0 +293,0 @@ return txBuilder; |
/// <reference types="bn.js" /> | ||
import { Instruction } from "@orca-so/common-sdk"; | ||
import { BN, Program } from "@project-serum/anchor"; | ||
import { u64 } from "@solana/spl-token"; | ||
import { Program } from "@project-serum/anchor"; | ||
import { PublicKey } from "@solana/web3.js"; | ||
import { Whirlpool } from "../artifacts/whirlpool"; | ||
import { Instruction } from "@orca-so/common-sdk"; | ||
import { PublicKey } from "@solana/web3.js"; | ||
import { BN } from "@project-serum/anchor"; | ||
/** | ||
@@ -63,2 +62,20 @@ * Parameters and accounts to swap on a Whirlpool | ||
/** | ||
* Parameters to swap on a Whirlpool with developer fees | ||
* | ||
* @category Instruction Types | ||
* @param aToB - The direction of the swap. True if swapping from A to B. False if swapping from B to A. | ||
* @param amountSpecifiedIsInput - Specifies the token the parameter `amount`represents. If true, the amount represents | ||
* the input token of the swap. | ||
* @param amount - The amount of input or output token to swap from (depending on amountSpecifiedIsInput). | ||
* @param otherAmountThreshold - The maximum/minimum of input/output token to swap into (depending on amountSpecifiedIsInput). | ||
* @param sqrtPriceLimit - The maximum/minimum price the swap will swap to. | ||
* @param tickArray0 - PublicKey of the tick-array where the Whirlpool's currentTickIndex resides in | ||
* @param tickArray1 - The next tick-array in the swap direction. If the swap will not reach the next tick-aray, input the same array as tickArray0. | ||
* @param tickArray2 - The next tick-array in the swap direction after tickArray2. If the swap will not reach the next tick-aray, input the same array as tickArray1. | ||
* @param devFeeAmount - FeeAmount (developer fees) charged on this swap | ||
*/ | ||
export declare type DevFeeSwapInput = SwapInput & { | ||
devFeeAmount: u64; | ||
}; | ||
/** | ||
* Perform a swap in this Whirlpool | ||
@@ -65,0 +82,0 @@ * |
/// <reference types="bn.js" /> | ||
import { Percentage } from "@orca-so/common-sdk"; | ||
import { Address, BN } from "@project-serum/anchor"; | ||
import { u64 } from "@solana/spl-token"; | ||
import { SwapInput } from "../../instructions"; | ||
import { WhirlpoolData, TickArray } from "../../types/public"; | ||
import { Percentage } from "@orca-so/common-sdk"; | ||
import { AccountFetcher } from "../../network/public"; | ||
import { TickArray, WhirlpoolData } from "../../types/public"; | ||
import { Whirlpool } from "../../whirlpool-client"; | ||
import { AccountFetcher } from "../../network/public"; | ||
import { DevFeeSwapQuote } from "./dev-fee-swap-quote"; | ||
/** | ||
@@ -32,2 +33,9 @@ * @category Quotes | ||
* @category Quotes | ||
* @link {BaseSwapQuote} | ||
* @link {DevFeeSwapQuote} | ||
*/ | ||
export declare type SwapQuote = NormalSwapQuote | DevFeeSwapQuote; | ||
/** | ||
* A collection of estimated values from quoting a swap. | ||
* @category Quotes | ||
* @param estimatedAmountIn - Approximate number of input token swapped in the swap | ||
@@ -39,3 +47,3 @@ * @param estimatedAmountOut - Approximate number of output token swapped in the swap | ||
*/ | ||
export declare type SwapQuote = { | ||
export declare type NormalSwapQuote = { | ||
estimatedAmountIn: u64; | ||
@@ -42,0 +50,0 @@ estimatedAmountOut: u64; |
@@ -16,8 +16,8 @@ "use strict"; | ||
exports.swapQuoteWithParams = exports.swapQuoteByOutputToken = exports.swapQuoteByInputToken = void 0; | ||
const common_sdk_1 = require("@orca-so/common-sdk"); | ||
const tiny_invariant_1 = __importDefault(require("tiny-invariant")); | ||
const pool_utils_1 = require("../../utils/public/pool-utils"); | ||
const common_sdk_1 = require("@orca-so/common-sdk"); | ||
const public_1 = require("../../utils/public"); | ||
const swap_utils_1 = require("../../utils/public/swap-utils"); | ||
const swap_quote_impl_1 = require("../swap/swap-quote-impl"); | ||
const swap_utils_1 = require("../../utils/public/swap-utils"); | ||
const swap_quote_utils_1 = require("../swap/swap-quote-utils"); | ||
/** | ||
@@ -38,3 +38,4 @@ * Get an estimated swap quote using input token amount. | ||
return __awaiter(this, void 0, void 0, function* () { | ||
return swapQuoteByToken(whirlpool, inputTokenMint, tokenAmount, slippageTolerance, public_1.TokenType.TokenA, true, programId, fetcher, refresh); | ||
const params = yield swapQuoteByToken(whirlpool, inputTokenMint, tokenAmount, public_1.TokenType.TokenA, true, programId, fetcher, refresh); | ||
return swapQuoteWithParams(params, slippageTolerance); | ||
}); | ||
@@ -61,3 +62,4 @@ } | ||
return __awaiter(this, void 0, void 0, function* () { | ||
return swapQuoteByToken(whirlpool, outputTokenMint, tokenAmount, slippageTolerance, public_1.TokenType.TokenB, false, programId, fetcher, refresh); | ||
const params = yield swapQuoteByToken(whirlpool, outputTokenMint, tokenAmount, public_1.TokenType.TokenB, false, programId, fetcher, refresh); | ||
return swapQuoteWithParams(params, slippageTolerance); | ||
}); | ||
@@ -75,17 +77,17 @@ } | ||
function swapQuoteWithParams(params, slippageTolerance) { | ||
checkIfAllTickArraysInitialized(params.tickArrays); | ||
(0, swap_quote_utils_1.checkIfAllTickArraysInitialized)(params.tickArrays); | ||
const quote = (0, swap_quote_impl_1.simulateSwap)(params); | ||
const slippageAdjustedQuote = Object.assign(Object.assign({}, quote), swap_utils_1.SwapUtils.calculateSwapAmountsFromQuote(params.tokenAmount, quote.estimatedAmountIn, quote.estimatedAmountOut, slippageTolerance, params.amountSpecifiedIsInput)); | ||
const slippageAdjustedQuote = Object.assign(Object.assign({}, quote), swap_utils_1.SwapUtils.calculateSwapAmountsFromQuote(quote.amount, quote.estimatedAmountIn, quote.estimatedAmountOut, slippageTolerance, quote.amountSpecifiedIsInput)); | ||
return slippageAdjustedQuote; | ||
} | ||
exports.swapQuoteWithParams = swapQuoteWithParams; | ||
function swapQuoteByToken(whirlpool, inputTokenMint, tokenAmount, slippageTolerance, amountSpecifiedTokenType, amountSpecifiedIsInput, programId, fetcher, refresh) { | ||
function swapQuoteByToken(whirlpool, inputTokenMint, tokenAmount, amountSpecifiedTokenType, amountSpecifiedIsInput, programId, fetcher, refresh) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const whirlpoolData = whirlpool.getData(); | ||
const swapMintKey = common_sdk_1.AddressUtil.toPubKey(inputTokenMint); | ||
const swapTokenType = pool_utils_1.PoolUtil.getTokenType(whirlpoolData, swapMintKey); | ||
const swapTokenType = public_1.PoolUtil.getTokenType(whirlpoolData, swapMintKey); | ||
(0, tiny_invariant_1.default)(!!swapTokenType, "swapTokenMint does not match any tokens on this pool"); | ||
const aToB = swapTokenType === amountSpecifiedTokenType; | ||
const tickArrays = yield swap_utils_1.SwapUtils.getTickArrays(whirlpoolData.tickCurrentIndex, whirlpoolData.tickSpacing, aToB, common_sdk_1.AddressUtil.toPubKey(programId), whirlpool.getAddress(), fetcher, refresh); | ||
return swapQuoteWithParams({ | ||
return { | ||
whirlpoolData, | ||
@@ -98,14 +100,4 @@ tokenAmount, | ||
tickArrays, | ||
}, slippageTolerance); | ||
}; | ||
}); | ||
} | ||
function checkIfAllTickArraysInitialized(tickArrays) { | ||
// Check if all the tick arrays have been initialized. | ||
const uninitializedIndices = public_1.TickArrayUtil.getUninitializedArrays(tickArrays.map((array) => array.data)); | ||
if (uninitializedIndices.length > 0) { | ||
const uninitializedArrays = uninitializedIndices | ||
.map((index) => tickArrays[index].address.toBase58()) | ||
.join(", "); | ||
throw new Error(`TickArray addresses - [${uninitializedArrays}] need to be initialized.`); | ||
} | ||
} |
@@ -5,4 +5,4 @@ import { Percentage, TransactionBuilder } from "@orca-so/common-sdk"; | ||
import { WhirlpoolContext } from "./context"; | ||
import { DevFeeSwapInput, SwapInput } from "./instructions"; | ||
import { AccountFetcher } from "./network/public"; | ||
import { SwapQuote } from "./quotes/public"; | ||
import { DecreaseLiquidityInput, IncreaseLiquidityInput, PositionData, WhirlpoolData } from "./types/public"; | ||
@@ -162,7 +162,17 @@ import { TokenAccountInfo, TokenInfo, WhirlpoolRewardInfo } from "./types/public/client-types"; | ||
* | ||
* @param quote - A quote on the desired tokenIn and tokenOut for this swap. Use @link {swapQuote} to generate this object. | ||
* @param input - A quote on the desired tokenIn and tokenOut for this swap. Use @link {swapQuote} to generate this object. | ||
* @param wallet - The wallet that tokens will be withdrawn and deposit into. If null, the WhirlpoolContext wallet is used. | ||
* @return a transaction that will perform the swap once executed. | ||
*/ | ||
swap: (quote: SwapQuote, wallet?: PublicKey) => Promise<TransactionBuilder>; | ||
swap: (input: SwapInput, wallet?: PublicKey) => Promise<TransactionBuilder>; | ||
/** | ||
* Collect a developer fee and perform a swap between tokenA and tokenB on this pool. | ||
* | ||
* @param input - A quote on the desired tokenIn and tokenOut for this swap. Use @link {swapQuote} to generate this object. | ||
* @param devFeeWallet - The wallet that developer fees will be deposited into. | ||
* @param wallet - The wallet that swap tokens will be withdrawn and deposit into. If null, the WhirlpoolContext wallet is used. | ||
* @param payer - The wallet that will fund the cost needed to initialize the dev wallet token ATA accounts. If null, the WhirlpoolContext wallet is used. | ||
* @return a transaction that will perform the swap once executed. | ||
*/ | ||
swapWithDevFees: (input: DevFeeSwapInput, devFeeWallet: PublicKey, wallet?: PublicKey, payer?: PublicKey) => Promise<TransactionBuilder>; | ||
} | ||
@@ -169,0 +179,0 @@ /** |
{ | ||
"name": "@orca-so/whirlpools-sdk", | ||
"version": "0.5.1", | ||
"version": "0.5.2", | ||
"description": "Typescript SDK to interact with Orca's Whirlpool program.", | ||
@@ -10,3 +10,3 @@ "license": "Apache-2.0", | ||
"@metaplex-foundation/mpl-token-metadata": "1.2.5", | ||
"@orca-so/common-sdk": "~0.1.0", | ||
"@orca-so/common-sdk": "~0.1.1", | ||
"@project-serum/anchor": "~0.25.0", | ||
@@ -18,3 +18,3 @@ "@solana/spl-token": "^0.1.8", | ||
"devDependencies": { | ||
"@types/bn.js": "^5.1.0", | ||
"@types/bn.js": "~5.1.0", | ||
"@types/decimal.js": "^7.4.0", | ||
@@ -21,0 +21,0 @@ "@types/jest": "^26.0.24", |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
12855
0
571084
142
Updated@orca-so/common-sdk@~0.1.1