@parifi/synthetix-sdk-ts
Version:
A Typescript SDK for interactions with the Synthetix protocol
739 lines (730 loc) • 31.5 kB
JavaScript
// src/spot/index.ts
import { encodeFunctionData as encodeFunctionData2, erc20Abi, getContract, maxUint256 } from "viem";
// src/constants/common.ts
import { zeroAddress } from "viem";
var ZERO_ADDRESS = zeroAddress;
var DISABLED_MARKETS = {
84532: [3, 6300],
8453: [6300]
};
var CUSTOM_DECIMALS = {
[421614 /* ARBITUM_SEPOLIA */]: {
2: 6,
USDC: 6
},
[42161 /* ARBITRUM */]: {
2: 6,
USDC: 6
},
[84532 /* BASE_SEPOLIA */]: {
1: 6,
USDC: 6
},
[8453 /* BASE */]: {
1: 6,
USDC: 6
}
};
// src/utils/common.ts
import { formatEther, maxUint128, parseEther } from "viem";
import { mainnet, base, optimism, arbitrum, baseSepolia, arbitrumSepolia } from "viem/chains";
// src/contracts/addreses/zap.ts
import { zeroAddress as zeroAddress2 } from "viem";
var ZAP_BY_CHAIN = {
[8453 /* BASE */]: "0x459Bbb4231f0a606DC514BacD5712A5922CBe6c8",
[84532 /* BASE_SEPOLIA */]: zeroAddress2,
[42161 /* ARBITRUM */]: "0x9ec181B2E69fB36C50031F0c87Bc0749b766A9f4",
[421614 /* ARBITUM_SEPOLIA */]: "0xb569ed692206a1d73996088ae646333b1d59d9c5"
};
var SYNTHETIX_ZAP = {
[84532 /* BASE_SEPOLIA */]: "0xA6Fab50eB36F3eB2118eE2f4C12C968F8608bbc9",
[8453 /* BASE */]: "0xA6Fab50eB36F3eB2118eE2f4C12C968F8608bbc9"
};
// src/utils/common.ts
function convertWeiToEther(amountInWei) {
if (amountInWei == void 0) {
throw new Error("Invalid amount received during conversion: undefined");
}
if (typeof amountInWei == "bigint") {
return Number(formatEther(amountInWei));
} else if (typeof amountInWei == "string") {
return Number(formatEther(BigInt(amountInWei)));
} else {
throw new Error("Expected string or bigint for conversion");
}
}
function convertEtherToWei(amount) {
if (amount == void 0) {
throw new Error("Invalid amount received during conversion: undefined");
}
if (typeof amount == "number") {
return parseEther(amount.toString());
} else if (typeof amount == "string") {
return parseEther(amount);
} else {
throw new Error("Expected string or bigint for conversion");
}
}
function sleep(seconds) {
return new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
}
// src/utils/index.ts
import {
decodeAbiParameters,
decodeErrorResult,
decodeFunctionResult,
encodeAbiParameters,
encodeFunctionData,
parseAbiParameters
} from "viem";
// src/contracts/addreses/oracleProxy.ts
var ORACLE_PROXY_BY_CHAIN = {
[421614 /* ARBITUM_SEPOLIA */]: "0x59d6ec32e05900949d7fff679a4adc7f94f0208c",
[42161 /* ARBITRUM */]: "0xe87ceB87b63267ef925E2897B629052eb815bB7d",
[8453 /* BASE */]: "0xa03A0Be2f3B85B76765A442683d62E992972d816",
[84532 /* BASE_SEPOLIA */]: "0x4Bc47c016EE6Ead253152CCfe6427D0bC06F544c"
};
// src/utils/market.ts
import { encodeAbiParameters as encodeAbiParameters2, parseUnits } from "viem";
var Market = class {
constructor(synthetixSdk) {
this.disabledMarkets = [];
this.sdk = synthetixSdk;
this.marketsById = /* @__PURE__ */ new Map();
this.marketsByName = /* @__PURE__ */ new Map();
this.marketsBySymbol = /* @__PURE__ */ new Map();
if (synthetixSdk.rpcConfig.chainId in DISABLED_MARKETS) {
this.disabledMarkets = DISABLED_MARKETS[synthetixSdk.rpcConfig.chainId];
}
}
/**
* Look up the market_id and market_name for a market. If only one is provided,
* the other is resolved. If both are provided, they are checked for consistency.
* @param marketIdOrName Id or name of the market to resolve
*/
async resolveMarket(marketIdOrName) {
if (!this.sdk.resolveMarketNames && typeof marketIdOrName === "number") {
console.log("Resolve markets set to false. Returning market id without validating...");
return { resolvedMarketName: "Unresolved Market", resolvedMarketId: Number(marketIdOrName) };
}
const market = await this.getMarket(marketIdOrName);
if (!market?.marketName || market?.marketId === void 0)
throw new Error(`Market not found for ${marketIdOrName}`);
return { resolvedMarketName: market.marketName, resolvedMarketId: market.marketId };
}
/**
* Format the size of a synth for an order. This is used for synths whose base asset
* does not use 18 decimals. For example, USDC uses 6 decimals, so we need to handle size
* differently from other assets.
* @param size The size as an ether value (e.g. 100).
* @param marketId The id of the market.
* @returns The formatted size in wei. (e.g. 100 = 100000000000000000000)
*/
async formatSize(size, marketId) {
return parseUnits(size.toString(), CUSTOM_DECIMALS[this.sdk.rpcConfig.chainId][marketId] || 18);
}
async prepareOracleCallsWithPriceId(priceFeedIds) {
if (!priceFeedIds.length) {
return [];
}
const stalenessTolerance = 30n;
const updateData = await this.sdk.pyth.getPriceFeedsUpdateData(priceFeedIds);
const signedRequiredData = encodeAbiParameters2(
[
{ type: "uint8", name: "updateType" },
{ type: "uint64", name: "stalenessTolerance" },
{ type: "bytes32[]", name: "priceIds" },
{ type: "bytes[]", name: "updateData" }
],
[1, stalenessTolerance, priceFeedIds, updateData]
);
const pythWrapper = await this.sdk.contracts.getPythErc7412WrapperInstance();
const dataVerificationTx = this.sdk.utils.generateDataVerificationTx(pythWrapper.address, signedRequiredData);
return [{ ...dataVerificationTx, value: 0n, requireSuccess: false }];
}
/**
* Prepare a call to the external node with oracle updates for the specified market names.
* The result can be passed as the first argument to a multicall function to improve performance
* of ERC-7412 calls. If no market names are provided, all markets are fetched. This is useful for
* read functions since the user does not pay gas for those oracle calls, and reduces RPC calls and
* runtime.
* @param {number[]} marketIds An array of market ids to fetch prices for. If not provided, all markets are fetched
* @returns {Promise<Call3Value[]>} objects representing the target contract, call data, value, requireSuccess flag and other necessary details for executing the function in the blockchain.
*/
async prepareOracleCall(marketIds = []) {
let priceFeedIds = [];
if (marketIds.length != 0) {
const marketSymbols = [];
marketIds.forEach((marketId) => {
const marketSymbol = this.marketsById.get(marketId)?.symbol;
if (!marketSymbol) return;
marketSymbols.push(marketSymbol);
});
marketSymbols.forEach((marketSymbol) => {
const feedId = this.sdk.pyth.priceFeedIds.get(marketSymbol);
if (!feedId) return;
priceFeedIds.push(feedId);
});
} else {
priceFeedIds = Array.from(this.sdk.pyth.priceFeedIds.values());
}
return this.prepareOracleCallsWithPriceId(priceFeedIds);
}
async getMarket(marketIdOrName) {
throw new Error("Method not implemented. " + marketIdOrName);
}
};
// src/spot/index.ts
var Spot = class extends Market {
constructor(synthetixSdk) {
super(synthetixSdk);
this.asyncOrderEnabled = false;
this.disabledMarkets = [];
this.sdk = synthetixSdk;
this.accountIds = [];
if (synthetixSdk.rpcConfig.chainId == 42161 || synthetixSdk.rpcConfig.chainId == 421614) {
this.asyncOrderEnabled = true;
}
}
async initSpot() {
await this.getMarkets();
}
async getMarket(marketIdOrName) {
const market = this.marketsById.get(Number(marketIdOrName)) ?? this.marketsByName.get(marketIdOrName);
if (!market) {
this.sdk.logger.warn(`Spot market ${marketIdOrName} not available. Available markets: ${this.marketsById}`);
throw new Error(`Invalid market id or name: ${marketIdOrName}`);
}
return market;
}
/**
* Fetches contracts and metadata about all spot markets on the network. This includes
* the market id, synth name, contract address, and the underlying synth contract. Each
* synth is an ERC20 token, so these contracts can be used for transfers and allowances.
* The metadata is also used to simplify interactions in the SDK by mapping market ids
* and names to their metadata
* For example:
* sdk.spot.wrap(100, market_name='sUSDC', submit=True)
* This will look up the market id for the sUSDC market and use that to wrap 100 USDC into sUSDC.
* The market metadata is returned from the method as a mapping object of two dictionaries/records.
* The first is keyed by ``marketsById`` and the second is keyed by ``marketsByName``
* For example: sdk.spot.marketsByName
* Response: {
'sUSD' => {
marketId: 0,
marketName: 'sUSD',
contractAddress: '0x682f0d17feDC62b2a0B91f8992243Bf44cAfeaaE'
},
* Example: sdk.spot.marketsById
* Response: {
0 => {
marketId: 0,
marketName: 'sUSD',
contractAddress: '0x682f0d17feDC62b2a0B91f8992243Bf44cAfeaaE'
},...
*/
async getMarkets() {
const usdProxy = await this.sdk.contracts.getUSDProxyInstance();
this.marketsById.set(0, {
marketId: 0,
marketName: "sUSD",
symbol: "sUSD",
contractAddress: usdProxy.address
});
this.marketsByName.set("sUSD", {
marketId: 0,
marketName: "sUSD",
symbol: "sUSD",
contractAddress: usdProxy.address
});
const spotProxy = await this.sdk.contracts.getSpotMarketProxyInstance();
const finalSynths = [];
const MAX_MARKETS = 100;
const ITEMS_PER_ITER = 5;
for (let index = 0; index < MAX_MARKETS; index += ITEMS_PER_ITER) {
const argsList = Array.from({ length: ITEMS_PER_ITER }, (_, i) => index + i);
const synthAddresses = await this.sdk.utils.multicallErc7412({
contractAddress: spotProxy.address,
abi: spotProxy.abi,
functionName: "getSynth",
args: argsList
});
synthAddresses.forEach((synthAddress, idx) => {
const isMarketDisabled = this.disabledMarkets.includes(argsList[idx]);
if (synthAddress != ZERO_ADDRESS && !isMarketDisabled) {
finalSynths.push({
marketId: argsList[idx],
contractAddress: synthAddress
});
}
});
if (synthAddresses.at(-1) == ZERO_ADDRESS) {
break;
}
}
let settlementStrategies;
if (this.asyncOrderEnabled) {
} else {
this.sdk.logger.info("Async orders not enabled on network ", this.sdk.rpcConfig.chainId);
}
const multicallInputs = [];
finalSynths.forEach((synth) => {
multicallInputs.push({
address: synth.contractAddress,
abi: erc20Abi,
functionName: "symbol"
});
});
const synthSymbols = await this.sdk.publicClient.multicall({
contracts: multicallInputs
});
finalSynths.forEach((synth, idx) => {
if (synthSymbols.at(idx)?.status == "success") {
const name = synthSymbols.at(idx)?.result;
synth.marketName = name;
synth.symbol = name.slice(1);
}
});
finalSynths.forEach((synth) => {
if (settlementStrategies != void 0 && settlementStrategies.length > 0) {
const strategy = settlementStrategies.find((strategy2) => strategy2.marketId == synth.marketId);
synth.settlementStrategy = strategy;
}
this.marketsById.set(synth.marketId, synth);
this.marketsByName.set(synth.marketName ?? "INVALID", synth);
});
return { marketsById: this.marketsById, marketsByName: this.marketsByName };
}
/**
* @name getSynthContract
* @description This function retrieves the Synth contract for a given market based on its ID or name. It returns the Synth contract instance.
* @param {MarketIdOrName} marketIdOrName - The unique identifier or name of the market.
* @returns {Contract<Synth>} - An instance of the Synth contract associated with the provided market.
*/
async getSynthContract(marketIdOrName) {
const { resolvedMarketId } = await this.resolveMarket(marketIdOrName);
const contractAddress = this.marketsById.get(resolvedMarketId)?.contractAddress;
if (contractAddress == void 0) {
throw new Error("Invalid market - contractAddress");
}
const synthContract = getContract({
address: contractAddress,
abi: erc20Abi,
client: this.sdk.publicClient
});
return synthContract;
}
/**
* Get the balance of a spot synth. Provide either a ``marketId`` or ``marketName``
* to choose the synth.
* @param {string} address The address to check the balance of. If not provided, the
* current account will be used.
* @param {MarketIdOrName} marketIdOrName - The unique identifier or name of the market.
* @returns {number} The balance of the synth in ether.
*/
async getBalance(address = this.sdk.accountAddress, marketIdOrName) {
const synthContract = await this.getSynthContract(marketIdOrName);
const balance = await synthContract.read.balanceOf([address]);
return convertWeiToEther(balance);
}
/**
* Get the allowance for a ``target_address`` to transfer from ``address``. Provide either
* a ``marketId`` or ``marketName`` to choose the synth.
* @param {string} targetAddress The address for which to check allowance.
* @param {string} address The owner address to check allowance for.
* @param {MarketIdOrName} marketIdOrName - The unique identifier or name of the market.
* @returns The allowance in ether.
*/
async getAllowance(targetAddress, address = this.sdk.accountAddress, marketIdOrName) {
const synthContract = await this.getSynthContract(marketIdOrName);
const allowance = await synthContract.read.allowance([address, targetAddress]);
return convertWeiToEther(allowance);
}
/**
* Get details about an async order by its ID.
* Retrieves order details like owner, amount escrowed, settlement strategy, etc.
* Can also fetch the full settlement strategy parameters if ``fetchSettlementStrategy``
* is ``true``.
* Requires either a ``market_id`` or ``market_name`` to be provided to resolve the market.
* @param {string} data.asyncOrderId The ID of the async order to retrieve.
* @param {MarketIdOrName} data.marketIdOrName - The unique identifier or name of the market.
* @param {boolean} data.fetchSettlementStrategy Whether to fetch the full settlement strategy parameters. Default is true.
* @returns {SpotOrder} The order details.
*/
async getOrder({
asyncOrderId,
marketIdOrName,
fetchSettlementStrategy = true
}) {
const { resolvedMarketId } = await this.resolveMarket(marketIdOrName);
const spotProxy = await this.sdk.contracts.getSpotMarketProxyInstance();
const order = await spotProxy.read.getAsyncOrderClaim([resolvedMarketId, asyncOrderId]);
this.sdk.logger.info("order", order);
if (fetchSettlementStrategy) {
const settlementStrategy = await this.getSettlementStrategy({
settlementStrategyId: Number(order.settlementStrategyId) || 0,
marketIdOrName: resolvedMarketId
});
order.settlementStrategy = settlementStrategy;
}
return order;
}
/**
* Fetch the settlement strategy for a spot market.
* @param {GetSettlementStrategy} data The data for fetching the settlement strategy.
* @param {number} data.settlementStrategyId The id of the settlement strategy to retrieve.
* @param {MarketIdOrName} data.marketIdOrName - The unique identifier or name of the market.
* @returns {SpotSettlementStrategy} The settlement strategy for the market.
*/
async getSettlementStrategy({
settlementStrategyId,
marketIdOrName
}) {
const { resolvedMarketId, resolvedMarketName } = await this.resolveMarket(marketIdOrName);
const spotProxy = await this.sdk.contracts.getSpotMarketProxyInstance();
const settlementStrategy = await this.sdk.utils.callErc7412({
contractAddress: spotProxy.address,
abi: spotProxy.abi,
functionName: "getSettlementStrategy",
args: [resolvedMarketId, settlementStrategyId]
});
return {
marketId: resolvedMarketId,
marketName: resolvedMarketName,
strategyType: settlementStrategy.strategyType,
settlementDelay: Number(settlementStrategy.settlementDelay),
settlementWindowDuration: Number(settlementStrategy.settlementWindowDuration),
priceVerificationContract: settlementStrategy.priceVerificationContract,
feedId: settlementStrategy.feedId,
url: settlementStrategy.url,
settlementReward: convertWeiToEther(settlementStrategy.settlementReward),
priceDeviationTolerance: convertWeiToEther(settlementStrategy.priceDeviationTolerance),
minimumUsdExchangeAmount: convertWeiToEther(settlementStrategy.minimumUsdExchangeAmount),
maxRoundingLoss: convertWeiToEther(settlementStrategy.maxRoundingLoss),
disabled: settlementStrategy.disabled
};
}
/**
* Fetch the settlement strategies for all spot markets.
* @param {GetSettlementStrategies} data The data for fetching the settlement strategies.
* @param {number} data.stragegyId The id of the settlement strategy to retrieve.
* @param {number[]} data.marketIds Array of marketIds to fetch settlement strategy
* @returns {SpotSettlementStrategy[]} The settlement strategies for the markets.
*/
async getSettlementStrategies({
settlementStrategyId: stragegyId,
marketIds
}) {
const spotProxy = await this.sdk.contracts.getSpotMarketProxyInstance();
const settlementStrategies = [];
let settlementStrategiesResponse;
const argsList = marketIds.map((marketId) => [marketId, stragegyId]);
const response = await this.sdk.utils.multicallErc7412({
contractAddress: spotProxy.address,
abi: spotProxy.abi,
functionName: "getSettlementStrategy",
args: argsList
});
if (response == void 0) {
settlementStrategiesResponse = [];
} else {
settlementStrategiesResponse = response;
}
this.sdk.logger.info("settlementStrategiesResponse", settlementStrategiesResponse);
settlementStrategiesResponse.forEach((strategy, index) => {
settlementStrategies.push({
marketId: marketIds[index],
strategyType: strategy.strategyType,
settlementDelay: Number(strategy.settlementDelay),
settlementWindowDuration: Number(strategy.settlementWindowDuration),
priceVerificationContract: strategy.priceVerificationContract,
feedId: strategy.feedId,
url: strategy.url,
settlementReward: convertWeiToEther(strategy.settlementReward),
priceDeviationTolerance: convertWeiToEther(strategy.priceDeviationTolerance),
minimumUsdExchangeAmount: convertWeiToEther(strategy.minimumUsdExchangeAmount),
maxRoundingLoss: convertWeiToEther(strategy.maxRoundingLoss),
disabled: strategy.disabled
});
});
return settlementStrategies;
}
// === WRITE CALLS ===
async buildAtomicOrder({
side,
size,
slippageTolerance = 0,
minAmountReceived,
marketIdOrName
}) {
const { resolvedMarketId, resolvedMarketName } = await this.resolveMarket(marketIdOrName);
const spotMarketProxy = await this.sdk.contracts.getSpotMarketProxyInstance();
if (this.sdk.rpcConfig.chainId in [8453, 84532] && resolvedMarketName in ["sUSDC"] && minAmountReceived == void 0) {
minAmountReceived = size;
} else if (minAmountReceived == void 0) {
const tokenSymbol = this.marketsById.get(resolvedMarketId)?.symbol;
if (tokenSymbol == void 0) {
throw new Error(`Invalid token symbol for market id: ${resolvedMarketId}`);
}
const feedId = this.sdk.pyth.priceFeedIds.get(tokenSymbol) ?? "0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a";
const price = await this.sdk.pyth.getFormattedPrice(feedId);
this.sdk.logger.info("Formatted price:", price);
let tradeSize;
if (side == 0 /* BUY */) {
tradeSize = size / price;
} else {
tradeSize = size * price;
}
minAmountReceived = tradeSize * (1 - slippageTolerance);
}
const minAmountReceivedInWei = convertEtherToWei(minAmountReceived);
const sizeInWei = convertEtherToWei(size);
const functionName = side == 0 /* BUY */ ? "buy" : "sell";
const args = [resolvedMarketId, sizeInWei, minAmountReceivedInWei, this.sdk.referrer];
return {
target: spotMarketProxy.address,
callData: encodeFunctionData2({
abi: spotMarketProxy.abi,
functionName,
args
}),
value: 0n,
requireSuccess: true
};
}
/**
* Execute an atomic order on the spot market.
* Atomically executes a buy or sell order for the given size.
* Amounts are transferred directly, no need to settle later. This function
* is useful for swapping sUSDC with sUSD on Base Andromeda contracts. The default
* slippage is set to zero, since sUSDC and sUSD can be swapped 1:1
* For example:
* const tx = await atomicOrder(Side.BUY, 100, 0, undefined, undefined, "sUSDC");
* Requires either a ``market_id`` or ``market_name`` to be provided to resolve the market.
* @param {AtomicOrder} data The data for the atomic order.
* @param {Side} data.side The side of the order (buy/sell).
* @param {number} data.size The order size in ether.
* @param {number} data.slippageTolerance The slippage tolerance for the order as a percentage (0.01 = 1%). Default is 0.
* @param {number} data.minAmountReceived The minimum amount to receive in ether units. This will override the slippage_tolerance.
* @param {MarketIdOrName} data.marketIdOrName - The unique identifier or name of the market.
* @param {OverrideParamsWrite} override - Override the default parameters for the transaction.
* @returns {WriteReturnType} The transaction hash if ``submit`` is ``true``.
*/
async atomicOrder({ side, size, slippageTolerance = 0, minAmountReceived, marketIdOrName }, override = {}) {
const atomicOrderTx = await this.buildAtomicOrder({
side,
size,
slippageTolerance,
minAmountReceived,
marketIdOrName
});
const txs = [atomicOrderTx];
return this.sdk.utils.processTransactions(txs, { ...override });
}
async buildWrap({ size, marketIdOrName }) {
const { resolvedMarketId } = await this.resolveMarket(marketIdOrName);
const spotMarketProxy = await this.sdk.contracts.getSpotMarketProxyInstance();
const tokenToWrap = resolvedMarketId;
const sizeInWei = await this.formatSize(Math.abs(size), tokenToWrap);
const functionName = size > 0 ? "wrap" : "unwrap";
const offset = sizeInWei * 100n / 1000n;
const wrapTx = {
target: spotMarketProxy.address,
callData: encodeFunctionData2({
abi: spotMarketProxy.abi,
functionName,
args: [resolvedMarketId, sizeInWei, sizeInWei - offset]
}),
value: 0n,
requireSuccess: true
};
return wrapTx;
}
/**
* Wrap an underlying asset into a synth or unwrap back to the asset.
* Wraps an asset into a synth if size > 0, unwraps if size < 0.
* The default slippage is set to zero, since the synth and asset can be swapped 1:1.
* For example:
* const tx = wrap(100, undefined, "sUSDC") # wrap 100 USDC into sUSDC
* const tx = wrap(-100, undefined, "sUSDC") # unwrap 100 sUSDC into USDC
* Requires either a ``market_id`` or ``market_name`` to be provided to resolve the market.
* @param {Wrap} data The data for the wrap/unwrap transaction.
* @param data.size The amount of the asset to wrap/unwrap.
* @param {MarketIdOrName} data.marketIdOrName - The unique identifier or name of the market.
* @param {OverrideParamsWrite} override - Override the default parameters for the transaction.
* @returns {WriteReturnType} The transaction hash if ``submit`` is ``true``.
*/
async wrap({ size, marketIdOrName }, override = {}) {
const wrapTx = await this.buildWrap({ size, marketIdOrName });
const txs = [wrapTx];
return this.sdk.utils.processTransactions(txs, { ...override });
}
async buildCommitOrder({
side,
size,
slippageTolerance,
minAmountReceived,
settlementStrategyId = 0,
marketIdOrName
}) {
const { resolvedMarketId } = await this.resolveMarket(marketIdOrName);
const spotMarketProxy = await this.sdk.contracts.getSpotMarketProxyInstance();
if (!minAmountReceived) {
const settlementReward = this.marketsById.get(resolvedMarketId)?.settlementStrategy?.settlementReward ?? 0;
const feedId = this.marketsById.get(resolvedMarketId)?.settlementStrategy?.feedId ?? "0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a";
const price = await this.sdk.pyth.getFormattedPrice(feedId);
this.sdk.logger.info("Formatted price:", price);
let tradeSize = size * price;
if (side == 0 /* BUY */) {
tradeSize = size / price;
}
minAmountReceived = tradeSize * (1 - slippageTolerance) - settlementReward;
}
const minAmountReceivedInWei = convertEtherToWei(minAmountReceived);
const sizeInWei = convertEtherToWei(size);
const orderType = side == 0 /* BUY */ ? 3 : 4;
const args = [
resolvedMarketId,
orderType,
sizeInWei,
settlementStrategyId,
minAmountReceivedInWei,
this.sdk.referrer
];
return {
target: spotMarketProxy.address,
callData: encodeFunctionData2({
abi: spotMarketProxy.abi,
functionName: "commitOrder",
args
}),
value: 0n,
requireSuccess: true
};
}
/**
* Commit an async order to the spot market.
* Commits a buy or sell order of the given size. The order will be settled
* according to the settlement strategy.
* Requires either a ``marketId`` or ``marketName`` to be provided to resolve the market.
* @param {CommitOrderSpot} data The data for the async order.
* @param {Side} data.side The side of the order (buy/sell).
* @param {number} data.size The order size in ether. If ``side`` is "buy", this is the amount
* of the synth to buy. If ``side`` is "sell", this is the amount of the synth to sell.
* @param {number} data.slippageTolerance The slippage tolerance for the order as a percentage (0.01 = 1%). Default is 0.
* @param {number} data.minAmountReceived The minimum amount to receive in ether units. This will override the slippage_tolerance.
* @param {number} data.settlementStrategyId The settlement strategy ID. Default 2.
* @param {MarketIdOrName} data.marketIdOrName - The unique identifier or name of the market.
* @param {OverrideParamsWrite} override - Override the default parameters for the transaction.
*/
async commitOrder({ side, size, slippageTolerance, minAmountReceived, settlementStrategyId = 0, marketIdOrName }, override = {}) {
const commitTx = await this.buildCommitOrder({
side,
size,
slippageTolerance,
minAmountReceived,
settlementStrategyId,
marketIdOrName
});
const txs = [commitTx];
return this.sdk.utils.processTransactions(txs, { ...override });
}
/**
* Settle an async Pyth order after price data is available.
* Fetches the price for the order from Pyth and settles the order.
* Retries up to ``maxTxTries`` times on failure with a delay of ``txDelay`` seconds.
* Requires either a ``marketId`` or ``marketName`` to be provided to resolve the market.
* @param {SettleOrder} data The data for the async order.
* @param {string} asyncOrderId The ID of the async order to settle.
* @param {MarketIdOrName} marketIdOrName The unique identifier or name of the market.
* @param {OverrideParamsWrite} override - Override the default parameters for the transaction.
* @returns {WriteReturnType} The transaction hash if ``submit`` is ``true``.
*/
async settleOrder({ asyncOrderId, marketIdOrName }, override = {}) {
const { resolvedMarketId } = await this.resolveMarket(marketIdOrName);
const spotMarketProxy = await this.sdk.contracts.getSpotMarketProxyInstance();
const order = await this.getOrder({ asyncOrderId, marketIdOrName: resolvedMarketId });
if (order.settledAt == void 0 || order.commitmentTime == void 0) {
throw new Error("Invalid fields for order: undefined");
}
const settlementStrategy = order.settlementStrategy;
const settlementTime = order.commitmentTime + (settlementStrategy?.settlementDelay ?? 0);
const expirationTime = order.commitmentTime + (settlementStrategy?.settlementWindowDuration ?? 0);
const currentTimestamp = Math.floor(Date.now() / 1e3);
if (order.settledAt > 0)
throw new Error(`Order ${asyncOrderId} on market ${resolvedMarketId} is already settled for account`);
if (expirationTime < currentTimestamp)
throw new Error(`Order ${asyncOrderId} on market ${resolvedMarketId} has expired`);
if (settlementTime > currentTimestamp) {
const duration = settlementTime - currentTimestamp;
this.sdk.logger.info(`Waiting ${duration} seconds to settle order`);
await sleep(duration);
}
this.sdk.logger.info(`Order ${asyncOrderId} on market ${resolvedMarketId} is ready to be settled`);
const settleTx = {
target: spotMarketProxy.address,
callData: encodeFunctionData2({
abi: spotMarketProxy.abi,
functionName: "settleOrder",
args: [resolvedMarketId, asyncOrderId]
}),
value: 0n,
requireSuccess: true
};
const txs = [settleTx];
return this.sdk.utils.processTransactions(txs, { ...override });
}
async buildApprove({
spender,
amount,
token
}) {
const amountInWei = convertEtherToWei(amount);
const tx = {
target: token,
callData: encodeFunctionData2({
abi: erc20Abi,
functionName: "approve",
args: [spender, amountInWei]
}),
value: 0n,
requireSuccess: true
};
return tx;
}
/**
* Approve an address to transfer a specified synth from the connected address.
* Approves the ``targetAddress`` to transfer up to the ``amount`` from your account.
* If ``amount`` is ``undefined``, approves the maximum possible amount.
* Requires either a ``marketId`` or ``marketName`` to be provided to resolve the market.
* @param {string} data.targetAddress The address to approve.
* @param {number} data.amount The amount in ether to approve. Default is max uint256.
* @param {MarketIdOrName} dart.marketIdOrName - The unique identifier or name of the market.
* @param {OverrideParamsWrite} override - Override the default parameters for the transaction.
* @returns {WriteReturnType} The transaction hash if ``submit`` is ``true``.
*/
async approve({ targetAddress, amount = 0, marketIdOrName }, override = {}) {
let amountInWei = maxUint256;
if (amount) {
amountInWei = convertEtherToWei(amount);
}
const synthContract = await this.getSynthContract(marketIdOrName);
const txs = [
{
target: synthContract.address,
callData: encodeFunctionData2({
abi: synthContract.abi,
functionName: "approve",
args: [targetAddress, amountInWei]
}),
value: 0n,
requireSuccess: true
}
];
return this.sdk.utils.processTransactions(txs, { ...override, useOracleCalls: false });
}
};
export {
Spot
};
//# sourceMappingURL=index.mjs.map