
Security News
Security Community Slams MIT-linked Report Claiming AI Powers 80% of Ransomware
Experts push back on new claims about AI-driven ransomware, warning that hype and sponsored research are distorting how the threat is understood.
@equilab/marginly-sdk
Advanced tools
SDK for the Marginly protocol
$ yarn add @equilab/marginly-sdk
This example will use Arbitrum mainnet and WETH/USDC marginly pool contract. BigNumber is imported from ethers.js
import { BigNumber, BigNumberish, ethers } from 'ethers';
const CHAIN_ID = 42161;
const POOL_ADDRESS = '0x87e711BcB9Ed1f2f6dec8fcC74cD2e0613D43b86';
import type { MarginlyPool } from '@equilab/marginly-sdk/abis/types/MarginlyPool';
import ABI from '@equilab/marginly-sdk/abis/marginly-pool.json';
const contract = useContract<MarginlyPool>(POOL_ADDRESS, ABI.abi, true);
This example is using updatedAt counter with some interval.
import { MarginlyCoeffs } from '@equilab/marginly-sdk';
const [basePriceX96, setBasePriceX96] = useState<BigNumber>();
const [coeffs, setCoeffs] = useState<MarginlyCoeffs>();
const [baseTokenAddress, setBaseTokenAddress] = useState<string>();
const [quoteTokenAddress, setQuoteTokenAddress] = useState<string>();
useEffect(() => {
  if (!contract) return;
  contract
    .baseToken()
    .then((res) => setBaseTokenAddress(res))
    .catch((e) => e instanceof Error && console.error(e));
  contract
    .quoteToken()
    .then((res) => setQuoteTokenAddress(res))
    .catch((e) => e instanceof Error && console.error(e));
  contract
    .getLiquidationPrice()
    .then((res) => {
      setBasePriceX96(BigNumber.from(res.inner));
    })
    .catch((e) => e instanceof Error && console.error(e));
  Promise.all([
    contract.baseCollateralCoeff(),
    contract.quoteCollateralCoeff(),
    contract.baseDelevCoeff(),
    contract.quoteDelevCoeff(),
    contract.baseDebtCoeff(),
    contract.quoteDebtCoeff(),
  ])
    .then(
      ([baseCollateralCoeff, quoteCollateralCoeff, baseDelevCoeff, quoteDelevCoeff, baseDebtCoeff, quoteDebtCoeff]) => {
        setCoeffs({
          baseCollateralCoeff,
          quoteCollateralCoeff,
          baseDelevCoeff,
          quoteDelevCoeff,
          baseDebtCoeff,
          quoteDebtCoeff,
        });
      }
    )
    .catch((e) => e instanceof Error && console.error(e));
}, [contract, updatedAt]);
There are 2 methods to get latest base price: getBasePrice and getLiquidationPrice. Both are twap prices with different intervals. getLiquidationPrice updates more frequently.
Use base and quote token addresses to request their parameters from chain.
import { useWeb3React } from '@web3-react/core';
const { account, provider } = useWeb3React();
const [position, setPosition] = useState<{
  _type: number;
  heapPosition: number;
  discountedBaseAmount: BigNumber;
  discountedQuoteAmount: BigNumber;
}>();
useEffect(() => {
  if (!account || !contract) return;
  contract
    .positions(account)
    .then((res) => {
      setPosition(res);
    })
    .catch((e) => e instanceof Error && console.error(e));
}, [contract, updatedAt, account]);
After recieving position and coeffs we can use MarginlyPosition class
import { MarginlyPosition } from '@equilab/marginly-sdk';
const derivedPosition = useMemo(() => {
  if (!position || !coeffs) return;
  return new MarginlyPosition(coeffs, position._type, position.discountedBaseAmount, position.discountedQuoteAmount);
}, [position, coeffs]);
Create transaction to open position. You need deposit amount and position amount to open position.
Long position example
Depositing 0.1 ETH (will be auto converted to WETH)
With leverage 5
means position amount equals 0.4
Short position example
Depositing 100 USDC
With leverage 5
means position amount equals [(5 - 1) * 100] / basePrice
When opening position limitPrice is provided so you can limit your slippage. Example below has slippage limit 5%. Limit price is in fixed point x96 format.
Depending on position direction getDepositBaseAndLongArgs or getDepositQuoteAndShortArgs method is used to prepare arguments.
import {
  convertPriceHumanToX96,
  convertPriceStringToX96,
  convertPriceX96ToHuman,
  getCalldata,
  getDepositBaseAndLongArgs,
  getDepositQuoteAndShortArgs,
} from '@equilab/marginly-sdk';
const openPositionTx = useMemo(() => {
  const limitPrice = basePrice
    ? direction === 'short'
      ? basePrice.toNumber() * 0.95
      : basePrice.toNumber() * 1.05
    : undefined;
  if (!limitPrice) return undefined;
  const depositAmount = ethers.utils.parseUnits(inputAmount || '0', depositToken.decimals);
  const positionAmount = ethers.utils.parseUnits(
    leveragedAmount.toFixed(direction === 'long' ? baseToken.decimals : quoteToken.decimals),
    baseToken.decimals
  );
  const limitPriceX96 = convertPriceStringToX96(
    limitPrice.toString(),
    BigNumber.from(baseToken.decimals),
    BigNumber.from(quoteToken.decimals)
  );
  const openMethod = direction === 'long' ? getDepositBaseAndLongArgs : getDepositQuoteAndShortArgs;
  const args = openMethod(depositAmount, positionAmount, limitPriceX96, ZERO, isNativeToken);
  const { calldata } = getCalldata(args);
  const tx = {
    from: account,
    to: POOL_ADDRESS,
    data: calldata,
    ...(isNativeToken ? { value: depositAmount } : {}),
  };
  return tx;
}, [account, inputAmount, basePrice, positionAmount, leveragedAmount, isNativeToken, isValidAmount]);
const closePositionTx = useMemo(() => {
  const limitPrice = basePrice
    ? derivedPosition.type === PositionType.Long
      ? basePrice.toNumber() * 0.95
      : basePrice.toNumber() * 1.05
    : undefined;
  if (!limitPrice) return undefined;
  const limitPriceX96 = convertPriceStringToX96(
    limitPrice.toString(),
    BigNumber.from(baseToken.decimals),
    BigNumber.from(quoteToken.decimals)
  );
  const args = getClosePositionArgs(limitPriceX96, ZERO, isNativeToken);
  const { calldata } = getCalldata(args);
  const tx = {
    from: account,
    to: POOL_ADDRESS,
    data: calldata,
  };
  return tx;
}, [derivedPosition, basePrice, baseToken, quoteToken, account, direction]);
After closing long/short position you should withdraw deposit
const withdrawAllTx = useMemo(() => {
  if (!derivedPosition || derivedPosition.type !== PositionType.Lend) return;
  const isWithdrawingBase = derivedPosition.baseAmount.gt(0);
  const args = isWithdrawingBase
    ? getWithdrawBaseAllArgs(baseToken?.isNative)
    : getWithdrawQuoteAllArgs(quoteToken?.isNative);
  const { calldata } = getCalldata(args);
  const tx = {
    from: account,
    to: POOL_ADDRESS,
    data: calldata,
  };
  return tx;
}, [derivedPosition, baseToken, quoteToken, account]);
This example will use Arbitrum mainnet and WETH/USDC marginly pool contract. BigNumber is imported from ethers.js
import {
  getActionArgs,
  getWithdrawAllArgs,
  MarginlyCoeffsBigInt,
  MarginlyPositionBigInt,
  PositionType,
} from '@equilab/marginly-sdk';
import ABI from '@equilab/marginly-sdk/abis/marginly-pool.json';
import { useAccount, useContractRead, useContractWrite, usePrepareContractWrite, useToken } from 'wagmi';
const CHAIN_ID = 42161;
const POOL_ADDRESS_WETH_USDC = '0x87e711BcB9Ed1f2f6dec8fcC74cD2e0613D43b86';
const contractInfo = {
  abi: ABI.abi,
  address: POOL_ADDRESS_WETH_USDC,
} as const;
const baseTokenAddress = useContractRead({
  ...contractInfo,
  functionName: 'baseToken',
});
const quoteTokenAddress = useContractRead({
  ...contractInfo,
  functionName: 'quoteToken',
});
const getLiquidationPrice = useContractRead({
  ...contractInfo,
  functionName: 'getLiquidationPrice',
});
const basePriceX96 = getLiquidationPrice.data?.inner || 0n;
const baseCollateralCoeff = useContractRead({
  ...contractInfo,
  functionName: 'baseCollateralCoeff',
});
const quoteCollateralCoeff = useContractRead({
  ...contractInfo,
  functionName: 'quoteCollateralCoeff',
});
const baseDelevCoeff = useContractRead({
  ...contractInfo,
  functionName: 'baseDelevCoeff',
});
const quoteDelevCoeff = useContractRead({
  ...contractInfo,
  functionName: 'quoteDelevCoeff',
});
const baseDebtCoeff = useContractRead({
  ...contractInfo,
  functionName: 'baseDebtCoeff',
});
const quoteDebtCoeff = useContractRead({
  ...contractInfo,
  functionName: 'quoteDebtCoeff',
});
const { data: baseToken } = useToken({ address: baseTokenAddress.data });
const { data: quoteToken } = useToken({ address: quoteTokenAddress.data });
There are 2 methods to get latest base price: getBasePrice and getLiquidationPrice. Both are twap prices with different intervals. getLiquidationPrice updates more frequently.
Use base and quote token addresses to request their parameters from chain.
const coeffs: MarginlyCoeffsBigInt = useMemo(
  () => ({
    baseCollateralCoeff: baseCollateralCoeff.data || 0n,
    baseDebtCoeff: baseDebtCoeff.data || 0n,
    quoteCollateralCoeff: quoteCollateralCoeff.data || 0n,
    quoteDebtCoeff: quoteDebtCoeff.data || 0n,
    baseDelevCoeff: baseDelevCoeff.data || 0n,
    quoteDelevCoeff: quoteDelevCoeff.data || 0n,
  }),
  [
    baseCollateralCoeff.data,
    baseDebtCoeff.data,
    baseDelevCoeff.data,
    quoteCollateralCoeff.data,
    quoteDebtCoeff.data,
    quoteDelevCoeff.data,
  ]
);
const positions = useContractRead({
  ...contractInfo,
  functionName: 'positions',
  args: [account.address],
});
const position: {
  _type: number;
  heapPosition: number;
  discountedBaseAmount: bigint;
  discountedQuoteAmount: bigint;
} = useMemo(
  () =>
    positions.data && {
      _type: positions.data[0],
      heapPosition: positions.data[1],
      discountedBaseAmount: positions.data[2],
      discountedQuoteAmount: positions.data[3],
    },
  [positions.data]
);
After recieving position and coeffs we can use MarginlyPosition class
const derivedPosition = useMemo(() => {
  if (!position || !coeffs || position._type === PositionType.Uninitialized) return;
  return new MarginlyPositionBigInt(
    coeffs,
    position._type,
    position.discountedBaseAmount,
    position.discountedQuoteAmount
  );
}, [position, coeffs]);
Create transaction to open position. You need deposit amount and position amount to open position.
Long position example
Depositing 0.1 ETH (will be auto converted to WETH)
With leverage 5
means position amount equals 0.4
Short position example
Depositing 100 USDC
With leverage 5
means position amount equals [(5 - 1) * 100] / basePrice
Depending on position direction getDepositBaseAndLongArgs or getDepositQuoteAndShortArgs method is used to prepare arguments.
Use getActionArgs helper to open long/short position.
const useOpen = ({
  accountAddress,
  baseToken,
  quoteToken,
  inputAmount,
  leverage,
  isNativeToken,
  basePriceX96,
  direction,
}: {
  accountAddress: `0x${string}` | undefined;
  baseToken: FetchTokenResult | undefined;
  quoteToken: FetchTokenResult | undefined;
  inputAmount: string;
  leverage: number;
  isNativeToken: boolean;
  basePriceX96: bigint;
  direction: "long" | "short";
}) => {
  const openPositionParamsWagmi = useMemo(() => {
    if (!baseToken || !quoteToken) return;
    return getActionArgs({
      type: "depositAndOpenPosition",
      baseDecimals: baseToken?.decimals,
      basePriceX96,
      depositAmount: inputAmount,
      direction,
      slippageTolerancePercentage: 1,
      leverage,
      isDepositingNativeToken: isNativeToken,
      quoteDecimals: quoteToken?.decimals,
      swapCalldata: 0n,
    });
  }, [
    basePriceX96,
    baseToken,
    direction,
    inputAmount,
    isNativeToken,
    leverage,
    quoteToken,
  ]);
  const { config: openPositionConfigWagmi } = usePrepareContractWrite(
    openPositionParamsWagmi
      ? {
          ...contractInfo,
          args: openPositionParamsWagmi.args,
          value: openPositionParamsWagmi.value,
          functionName: openPositionParamsWagmi.methodName,
          account: accountAddress,
          gas: undefined,
        }
      : undefined,
  );
  const openPositionTx = useContractWrite(openPositionConfigWagmi);
  const handleOpen = () => {
    openPositionTx.writeAsync?.();
  };
  return handleOpen;
};
...
// long and short examples
 const handleOpenLong = useOpen({
    accountAddress: account.address,
    basePriceX96,
    baseToken,
    direction: "long",
    inputAmount: inputLongAmount,
    isNativeToken,
    leverage,
    quoteToken,
  });
  const handleOpenShort = useOpen({
    accountAddress: account.address,
    basePriceX96,
    baseToken,
    direction: "short",
    inputAmount: inputShortAmount,
    isNativeToken: false,
    leverage,
    quoteToken,
  });
const useClose = ({
  accountAddress,
  baseToken,
  quoteToken,
  isNativeToken,
  basePriceX96,
  type,
}: {
  accountAddress: `0x${string}` | undefined;
  baseToken: FetchTokenResult | undefined;
  quoteToken: FetchTokenResult | undefined;
  isNativeToken: boolean;
  basePriceX96: bigint;
  type: PositionType | undefined;
}) => {
  const closePositionParams = useMemo(() => {
    if (!baseToken || !quoteToken) return;
    return getActionArgs({
      baseDecimals: baseToken?.decimals,
      basePriceX96,
      direction: type === PositionType.Long ? 'long' : 'short',
      isDepositingNativeToken: isNativeToken,
      quoteDecimals: quoteToken.decimals,
      slippageTolerancePercentage: 5,
      swapCalldata: 0n,
      type: 'close',
    });
  }, [basePriceX96, baseToken, isNativeToken, quoteToken, type]);
  const { config: closePositionConfig } = usePrepareContractWrite(
    closePositionParams
      ? {
          ...contractInfo,
          args: closePositionParams.args,
          value: closePositionParams.value,
          functionName: closePositionParams.methodName,
          account: accountAddress,
          gas: undefined,
        }
      : undefined
  );
  const closePositionTx = useContractWrite(closePositionConfig);
  const handleClose = () => {
    closePositionTx.writeAsync?.();
  };
  return handleClose;
};
When long or short position is closed user has lend position with his deposit.
To withdraw deposit from lend position use getWithdrawAllArgs helper to prepare contract call arguments
const useWithdraw = ({
  accountAddress,
  derivedPosition,
  isNativeToken,
}: {
  accountAddress: `0x${string}` | undefined;
  derivedPosition: MarginlyPositionBigInt | undefined;
  isNativeToken: boolean;
}) => {
  const withdrawAllParams = useMemo(() => {
    return getWithdrawAllArgs(derivedPosition, isNativeToken, !isNativeToken);
  }, [derivedPosition, isNativeToken]);
  const { config: withdrawPositionConfig } = usePrepareContractWrite(
    withdrawAllParams
      ? {
          ...contractInfo,
          args: withdrawAllParams.args,
          value: withdrawAllParams.value,
          functionName: withdrawAllParams.methodName,
          account: accountAddress,
          gas: undefined,
        }
      : undefined
  );
  const withdrawPositionTx = useContractWrite(withdrawPositionConfig);
  const handleWithdraw = () => {
    withdrawPositionTx.writeAsync?.();
  };
  return handleWithdraw;
};
FAQs
SDK for the Marginly protocol
We found that @equilab/marginly-sdk demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Experts push back on new claims about AI-driven ransomware, warning that hype and sponsored research are distorting how the threat is understood.

Security News
Ruby's creator Matz assumes control of RubyGems and Bundler repositories while former maintainers agree to step back and transfer all rights to end the dispute.

Research
/Security News
Socket researchers found 10 typosquatted npm packages that auto-run on install, show fake CAPTCHAs, fingerprint by IP, and deploy a credential stealer.