@sharwa.finance/hegic-stop-orders-sdk
Version:
HegicStopOrdersSDK is a specialized SDK package designed to facilitate interaction with HegicStopOrders contracts. It enables the automatic execution of options based on time and price triggers.
367 lines (304 loc) • 15.7 kB
text/typescript
import { Contract, Provider, Signer, EventLog, Log, Interface, ZeroAddress, TransactionResponse } from "ethers";
import conf from "./config.json";
import abi_take_profit from "./abi/abi_take_profit.json";
import abi_positions_manager from "./abi/abi_positions_manager.json";
import abi_operational_treasury from "./abi/abi_operational_treasury.json"
type TakeProfitData = {
tokenId: bigint
upperStopPrice: number
lowerStopPrice: number
blockNumber: number
}
type TransferData = {
tokenId: bigint,
blockNumber: number
}
export class TakeProfitSharwaFinance {
signer: Signer
provider: Provider
take_profit: Contract
positions_manager: Contract
operational_treasury: Contract
iface: Interface
constructor(provider: Provider, signer: Signer) {
this.provider = provider
this.signer = signer
this.take_profit = new Contract(conf.take_profit_address, abi_take_profit, signer)
this.positions_manager = new Contract(conf.positions_manager_address, abi_positions_manager, signer)
this.operational_treasury = new Contract(conf.operational_treasury_address, abi_operational_treasury, signer)
this.iface = new Interface(abi_take_profit)
}
// STATE CHANGE FUNCTIONS //
async setTakeProfit(tokenId: bigint, upperStopPrice: bigint, lowerStopPrice: bigint): Promise<TransactionResponse> {
return await this.take_profit.setTakeProfit(tokenId, {upperStopPrice, lowerStopPrice})
}
async deleteTakeProfit(tokenId: bigint): Promise<TransactionResponse> {
return await this.take_profit.deleteTakeProfit(tokenId)
}
async enableAutoExecutionForAllOptions(): Promise<TransactionResponse> {
return await this.positions_manager.setApprovalForAll(conf.take_profit_address, true)
}
async disableAutoExecutionForAllOptions(): Promise<TransactionResponse> {
return await this.positions_manager.setApprovalForAll(conf.take_profit_address, false)
}
async enableAutoExecutionForOption(tokenId: bigint): Promise<TransactionResponse> {
return await this.positions_manager.approve(conf.take_profit_address, tokenId)
}
async disableAutoExecutionForOption(tokenId: bigint): Promise<TransactionResponse> {
return await this.positions_manager.approve(ZeroAddress, tokenId)
}
// FILTER_OPTIONS FUNCTIONS //
async filterOptionsWithAutoExecutionEnabled(user: string, arrAcivaOptions: bigint[]): Promise<bigint[]> {
const ApprovalFilter = this.positions_manager.filters.Approval(user, conf.take_profit_address, null)
const arrApproval = await this.positions_manager.queryFilter(ApprovalFilter)
const uniqApprovalLogs = this._uniqTransferData(arrApproval)
const DisableApprovalFilter = this.positions_manager.filters.Approval(user, ZeroAddress, null)
const arrDisableApproval = await this.positions_manager.queryFilter(DisableApprovalFilter)
const uniqDisableApprovalLogs = this._uniqTransferData(arrDisableApproval)
const ApprovalForAllFilter = this.positions_manager.filters.ApprovalForAll(user, conf.take_profit_address, null)
const arrApprovalForAll = await this.positions_manager.queryFilter(ApprovalForAllFilter)
const isApprovalForAll = this._isApprovalForAllData(arrApprovalForAll)
const autoExecutionOptions: bigint[] = []
for (const i in arrAcivaOptions) {
const logApproval = uniqApprovalLogs.find(elem=> elem.tokenId === arrAcivaOptions[i]);
const logDisableApproval = uniqDisableApprovalLogs.find(elem=> elem.tokenId === arrAcivaOptions[i]);
let isApproved: boolean = true
if (logDisableApproval != undefined && logApproval != undefined) {
if (logDisableApproval.blockNumber > logApproval.blockNumber) {
isApproved = false
}
}
if (isApprovalForAll) {
autoExecutionOptions.push(arrAcivaOptions[i])
} else {
if (logApproval != undefined && isApproved) {
autoExecutionOptions.push(arrAcivaOptions[i])
}
}
}
return autoExecutionOptions
}
async filterOptionsWithTakeProfits(arrAcivaOptions: bigint[]): Promise<Map<bigint,TakeProfitData>> {
let activeTakeProfits = new Map()
const TakeProfitSetFilter = this.take_profit.filters.TakeProfitSet(arrAcivaOptions, null)
const arrLogsTakeProfitSet = await this.take_profit.queryFilter(TakeProfitSetFilter)
const uniqSetLogs = this._uniqTakeProfitData(arrLogsTakeProfitSet)
for (const i in arrAcivaOptions) {
const logSet = uniqSetLogs.find(elem=> elem.tokenId === arrAcivaOptions[i]);
const TakeProfitDeletedFilter = this.take_profit.filters.TakeProfitDeleted(arrAcivaOptions[i])
const logTakeProfitDeleted = await this.take_profit.queryFilter(TakeProfitDeletedFilter)
const TakeProfitExecutedFilter = this.take_profit.filters.TakeProfitExecuted(arrAcivaOptions[i], null)
const logTakeProfitExecuted = await this.take_profit.queryFilter(TakeProfitExecutedFilter)
const lastDeleteBlockNumber = logTakeProfitDeleted.length == 0 ?
0 :
logTakeProfitDeleted[logTakeProfitDeleted.length-1].blockNumber
if (logSet != undefined) {
if (lastDeleteBlockNumber < logSet.blockNumber && logTakeProfitExecuted.length == 0) {
if ((logSet.upperStopPrice == 0 && logSet.lowerStopPrice == 0) == false) {
activeTakeProfits.set(
logSet.tokenId, {
upperStopPrice: logSet.upperStopPrice,
lowerStopPrice: logSet.lowerStopPrice
})
}
}
}
}
return activeTakeProfits
}
async getAutoExecutedOptions(arrAcivaOptions: bigint[]): Promise<bigint[]> {
let executeTakeProfits: bigint[] = []
for (const i in arrAcivaOptions) {
const TakeProfitExecutedFilter = this.take_profit.filters.TakeProfitExecuted(arrAcivaOptions[i], null)
const logTakeProfitExecuted = await this.take_profit.queryFilter(TakeProfitExecutedFilter)
if (logTakeProfitExecuted.length != 0) {
executeTakeProfits.push(arrAcivaOptions[i])
}
}
return executeTakeProfits
}
// DATA QUERY FUNCTIONS //
async isAutoExecutionEnabledForOption(tokenId: bigint): Promise<boolean> {
return await this.positions_manager.isApprovedOrOwner(conf.take_profit_address, tokenId)
}
async isAutoExecutionEnabledForAllOptions(user: string): Promise<boolean> {
const ApprovalForAllFilter = this.positions_manager.filters.ApprovalForAll(user, conf.take_profit_address, null)
const arrApprovalForAll = await this.positions_manager.queryFilter(ApprovalForAllFilter)
return this._isApprovalForAllData(arrApprovalForAll)
}
async _getActiveTakeProfits(user: string): Promise<Map<bigint,TakeProfitData>> {
let activeTakeProfits = new Map()
const userSetLogs = await this._getUserSetLogs(user)
for (const i in userSetLogs) {
const TakeProfitDeletedFilter = this.take_profit.filters.TakeProfitDeleted(userSetLogs[i].tokenId)
const logTakeProfitDeleted = await this.take_profit.queryFilter(TakeProfitDeletedFilter)
const TakeProfitExecutedFilter = this.take_profit.filters.TakeProfitExecuted(userSetLogs[i].tokenId, null)
const logTakeProfitExecuted = await this.take_profit.queryFilter(TakeProfitExecutedFilter)
const lastDeleteBlockNumber = logTakeProfitDeleted.length == 0 ?
0 :
logTakeProfitDeleted[logTakeProfitDeleted.length-1].blockNumber
if (lastDeleteBlockNumber < userSetLogs[i].blockNumber && logTakeProfitExecuted.length == 0) {
if ((userSetLogs[i].upperStopPrice == 0 && userSetLogs[i].lowerStopPrice == 0) == false) {
activeTakeProfits.set(
userSetLogs[i].tokenId, {
upperStopPrice: userSetLogs[i].upperStopPrice,
lowerStopPrice: userSetLogs[i].lowerStopPrice
})
}
}
}
return activeTakeProfits
}
async _getAllTakeProfits(user: string): Promise<Map<bigint,TakeProfitData>> {
let allTakeProfits = new Map()
const userSetLogs = await this._getUserSetLogs(user)
for (const i in userSetLogs) {
const TakeProfitDeletedFilter = this.take_profit.filters.TakeProfitDeleted(userSetLogs[i].tokenId)
const logTakeProfitDeleted = await this.take_profit.queryFilter(TakeProfitDeletedFilter)
const lastDeleteBlockNumber = logTakeProfitDeleted.length == 0 ?
0 :
logTakeProfitDeleted[logTakeProfitDeleted.length-1].blockNumber
if (lastDeleteBlockNumber < userSetLogs[i].blockNumber) {
allTakeProfits.set(
userSetLogs[i].tokenId, {
upperStopPrice: userSetLogs[i].upperStopPrice,
lowerStopPrice: userSetLogs[i].lowerStopPrice
})
}
}
return allTakeProfits
}
async _getExecuteTakeProfits(user: string): Promise<bigint[]> {
const TakeProfitExecutedFilter = this.take_profit.filters.TakeProfitExecuted(null, user)
const arrLogsTakeProfitExecuted = (await this.take_profit.queryFilter(TakeProfitExecutedFilter)).reverse()
let executeTakeProfits: bigint[] = []
for (const i in arrLogsTakeProfitExecuted) {
const log = arrLogsTakeProfitExecuted[i]
if (log instanceof EventLog) {
const arg = log.args
executeTakeProfits.push(arg.tokenId)
}
}
return executeTakeProfits
}
async _getUserSetLogs(user: string): Promise<TakeProfitData[]> {
const TransferInFilter = this.positions_manager.filters.Transfer(null, user, null)
const arrLogsTransferIn = await this.positions_manager.queryFilter(TransferInFilter)
const uniqTransfersIn = this._uniqTransferData(arrLogsTransferIn)
const TransferOutFilter = this.positions_manager.filters.Transfer(user, null, null)
const arrLogsTransferOut = await this.positions_manager.queryFilter(TransferOutFilter)
const uniqTransfersOut = this._uniqTransferData(arrLogsTransferOut)
const TakeProfitSetFilter = this.take_profit.filters.TakeProfitSet(null, user)
const arrLogsTakeProfitSet = await this.take_profit.queryFilter(TakeProfitSetFilter)
const uniqSetLogs = this._uniqTakeProfitData(arrLogsTakeProfitSet)
const userSetLogs: TakeProfitData[] = []
for (const i in uniqTransfersIn) {
const logIn = uniqTransfersIn.find(elem=> elem.tokenId === uniqTransfersIn[i].tokenId);
const logOut = uniqTransfersOut.find(elem=> elem.tokenId === uniqTransfersIn[i].tokenId);
const logSet = uniqSetLogs.find(elem=> elem.tokenId === uniqTransfersIn[i].tokenId);
if (logIn != undefined && logOut != undefined) {
if (logIn.blockNumber > logOut.blockNumber && logSet != undefined) {
userSetLogs.push(logSet)
}
} else if (logOut == undefined) {
if (logIn != undefined && logSet != undefined) {
userSetLogs.push(logSet)
}
}
}
return userSetLogs
}
// PRIVATE FUNCTIONS //
private _isApprovalForAllData(array: (EventLog | Log)[]): boolean {
if (array.length == 0) {
return false
}
const map = new Map();
array.forEach(item => {
if (item instanceof EventLog) {
map.set(item.blockNumber, item.args.approved);
} else if (item instanceof Log) {
const data = item.data
const topics = [...item.topics]
const decodeLog = this.iface.parseLog({data, topics})
if (decodeLog === null) {
throw new Error("Failed to decode log");
}
map.set(item.blockNumber, decodeLog.args.approved);
}
})
const returnArr = Array.from(map.values())
return returnArr[returnArr.length-1];
}
private _uniqArrayData(array: (EventLog | Log)[]): bigint[] {
const arr: bigint[] = []
array.forEach(item => {
if (item instanceof EventLog) {
arr.push(item.args[0])
} else if (item instanceof Log) {
const data = item.data
const topics = [...item.topics]
const decodeLog = this.iface.parseLog({data, topics})
if (decodeLog === null) {
throw new Error("Failed to decode log");
}
arr.push(decodeLog.args[0])
}
})
return arr
}
private _uniqTransferData(array: (EventLog | Log)[]): TransferData[] {
const map = new Map<bigint,TransferData>();
array.forEach(item => {
let decodeData: TransferData = {} as TransferData;
if (item instanceof EventLog) {
decodeData = {
tokenId: item.args.tokenId,
blockNumber: item.blockNumber
}
} else if (item instanceof Log) {
const data = item.data
const topics = [...item.topics]
const decodeLog = this.iface.parseLog({data, topics})
if (decodeLog === null) {
throw new Error("Failed to decode log");
}
decodeData = {
tokenId: decodeLog.args.tokenId,
blockNumber: item.blockNumber
}
}
map.set(decodeData.tokenId, decodeData);
})
return Array.from(map.values());
}
private _uniqTakeProfitData(array: (EventLog | Log)[]): TakeProfitData[] {
const map = new Map<bigint, TakeProfitData>();
array.forEach(item => {
let decodeData: TakeProfitData = {} as TakeProfitData;
if (item instanceof EventLog) {
decodeData = {
tokenId: item.args.tokenId,
upperStopPrice: item.args.upperStopPrice,
lowerStopPrice: item.args.lowerStopPrice,
blockNumber: item.blockNumber
}
} else if (item instanceof Log) {
const data = item.data
const topics = [...item.topics]
const decodeLog = this.iface.parseLog({data, topics})
if (decodeLog === null) {
throw new Error("Failed to decode log");
}
decodeData = {
tokenId: decodeLog.args.tokenId,
upperStopPrice: decodeLog.args.upperStopPrice,
lowerStopPrice: decodeLog.args.lowerStopPrice,
blockNumber: item.blockNumber
};
}
map.set(decodeData.tokenId, decodeData);
})
return Array.from(map.values());
}
}