@ethersproject/constants#MaxUint256 TypeScript Examples

The following examples show how to use @ethersproject/constants#MaxUint256. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From core with GNU General Public License v3.0 6 votes vote down vote up
export async function approveCurrency(
  currency: string,
  spender: string,
  owner: Wallet
) {
  await BaseErc20Factory.connect(currency, owner).approve(spender, MaxUint256);
}
Example #2
Source File: byterepr.ts    From clarity with Apache License 2.0 6 votes vote down vote up
toBytesNumber = (
  bitSize: number,
  signed: boolean,
  value: BigNumberish
) => {
  let v = BigNumber.from(value);

  // Check bounds are safe for encoding
  const maxUintValue = MaxUint256.mask(bitSize);
  if (signed) {
    const bounds = maxUintValue.mask(bitSize - 1); // 1 bit for signed
    if (v.gt(bounds) || v.lt(bounds.add(One).mul(NegativeOne))) {
      throw new Error('value out-of-bounds, value: ' + value);
    }
  } else if (v.lt(Zero) || v.gt(maxUintValue.mask(bitSize))) {
    throw new Error('value out-of-bounds, value: ' + value);
  }
  v = v.toTwos(bitSize).mask(bitSize);
  const bytes = arrayify(v);
  if (v.gte(0)) {
    // for positive number, we had to deal with paddings
    if (bitSize > 64) {
      // for u128, u256, u512, we have to and append extra byte for length
      return concat([bytes, Uint8Array.from([bytes.length])]).reverse();
    } else {
      // for other types, we have to add padding 0s
      const byteLength = bitSize / 8;
      return concat([
        bytes.reverse(),
        new Uint8Array(byteLength - bytes.length)
      ]);
    }
  } else {
    return bytes.reverse();
  }
}
Example #3
Source File: useApproveCallback.ts    From mozartfinance-swap-interface with GNU General Public License v3.0 5 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React()
  const token = amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined
  const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
  const pendingApproval = useHasPendingApproval(token?.address, spender)

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
    if (amountToApprove.currency === ETHER) return ApprovalState.APPROVED
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED
  }, [amountToApprove, currentAllowance, pendingApproval, spender])

  const tokenContract = useTokenContract(token?.address)
  const addTransaction = useTransactionAdder()

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error('approve was called unnecessarily')
      return
    }
    if (!token) {
      console.error('no token')
      return
    }

    if (!tokenContract) {
      console.error('tokenContract is null')
      return
    }

    if (!amountToApprove) {
      console.error('missing amount to approve')
      return
    }

    if (!spender) {
      console.error('no spender')
      return
    }

    let useExact = false
    const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
      // general fallback for tokens who restrict approval amounts
      useExact = true
      return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
    })

    // eslint-disable-next-line consistent-return
    return tokenContract
      .approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256, {
        gasLimit: calculateGasMargin(estimatedGas),
      })
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: `Approve ${amountToApprove.currency.symbol}`,
          approval: { tokenAddress: token.address, spender },
        })
      })
      .catch((error: Error) => {
        console.error('Failed to approve token', error)
        throw error
      })
  }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction])

  return [approvalState, approve]
}
Example #4
Source File: useApproveCallback.ts    From pancakeswap-testnet with GNU General Public License v3.0 5 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React()
  const token = amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined
  const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
  const pendingApproval = useHasPendingApproval(token?.address, spender)

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
    if (amountToApprove.currency === ETHER) return ApprovalState.APPROVED
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED
  }, [amountToApprove, currentAllowance, pendingApproval, spender])

  const tokenContract = useTokenContract(token?.address)
  const addTransaction = useTransactionAdder()

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error('approve was called unnecessarily')
      return
    }
    if (!token) {
      console.error('no token')
      return
    }

    if (!tokenContract) {
      console.error('tokenContract is null')
      return
    }

    if (!amountToApprove) {
      console.error('missing amount to approve')
      return
    }

    if (!spender) {
      console.error('no spender')
      return
    }

    let useExact = false
    const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
      // general fallback for tokens who restrict approval amounts
      useExact = true
      return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
    })

    // eslint-disable-next-line consistent-return
    return tokenContract
      .approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256, {
        gasLimit: calculateGasMargin(estimatedGas),
      })
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: `Approve ${amountToApprove.currency.symbol}`,
          approval: { tokenAddress: token.address, spender },
        })
      })
      .catch((error: Error) => {
        console.error('Failed to approve token', error)
        throw error
      })
  }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction])

  return [approvalState, approve]
}
Example #5
Source File: useApproveCallback.ts    From luaswap-interface with GNU General Public License v3.0 5 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React()
  const token = amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined
  const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
  const pendingApproval = useHasPendingApproval(token?.address, spender)

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
    if (amountToApprove.currency === ETHER || amountToApprove.currency === TOMO) return ApprovalState.APPROVED
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED
  }, [amountToApprove, currentAllowance, pendingApproval, spender])

  const tokenContract = useTokenContract(token?.address)
  const addTransaction = useTransactionAdder()

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error('approve was called unnecessarily')
      return
    }
    if (!token) {
      console.error('no token')
      return
    }

    if (!tokenContract) {
      console.error('tokenContract is null')
      return
    }

    if (!amountToApprove) {
      console.error('missing amount to approve')
      return
    }

    if (!spender) {
      console.error('no spender')
      return
    }

    let useExact = false
    const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
      // general fallback for tokens who restrict approval amounts
      useExact = true
      return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
    })

    return tokenContract
      .approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256, {
        gasLimit: calculateGasMargin(estimatedGas)
      })
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: 'Approve ' + amountToApprove.currency.symbol,
          approval: { tokenAddress: token.address, spender: spender }
        })
      })
      .catch((error: Error) => {
        console.debug('Failed to approve token', error)
        throw error
      })
  }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction])

  return [approvalState, approve]
}
Example #6
Source File: useApproveCallback.ts    From panther-frontend-dex with GNU General Public License v3.0 5 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React()
  const token = amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined
  const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
  const pendingApproval = useHasPendingApproval(token?.address, spender)

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
    if (amountToApprove.currency === ETHER) return ApprovalState.APPROVED
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED
  }, [amountToApprove, currentAllowance, pendingApproval, spender])

  const tokenContract = useTokenContract(token?.address)
  const addTransaction = useTransactionAdder()

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error('approve was called unnecessarily')
      return
    }
    if (!token) {
      console.error('no token')
      return
    }

    if (!tokenContract) {
      console.error('tokenContract is null')
      return
    }

    if (!amountToApprove) {
      console.error('missing amount to approve')
      return
    }

    if (!spender) {
      console.error('no spender')
      return
    }

    let useExact = false
    const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
      // general fallback for tokens who restrict approval amounts
      useExact = true
      return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
    })

    // eslint-disable-next-line consistent-return
    return tokenContract
      .approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256, {
        gasLimit: calculateGasMargin(estimatedGas),
      })
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: `Approve ${amountToApprove.currency.symbol}`,
          approval: { tokenAddress: token.address, spender },
        })
      })
      .catch((error: Error) => {
        console.error('Failed to approve token', error)
        throw error
      })
  }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction])

  return [approvalState, approve]
}
Example #7
Source File: currency.ts    From zora-v1-subgraph with MIT License 5 votes vote down vote up
export async function approveCurrency(
  wallet: Wallet,
  tokenAddress: string,
  to: string
): Promise<void> {
  const tx = await BaseErc20Factory.connect(tokenAddress, wallet).approve(to, MaxUint256)
  await tx.wait()
}
Example #8
Source File: currency.ts    From zora-v1-subgraph with MIT License 5 votes vote down vote up
async function start() {
  const provider = new JsonRpcProvider(process.env.RPC_ENDPOINT)
  const args = require('minimist')(process.argv.slice(2))

  let [wallet1, wallet2, wallet3, wallet4, wallet5] = generatedWallets(provider)

  switch (args.funcName) {
    case 'deploy': {
      console.log('Deploying Bid Currency...')
      const currencyAddress = await deployCurrency(wallet2)
      console.log(`Bid Currency deployed at ${currencyAddress}`)
      break
    }
    case 'allow': {
      if (!args.tokenAddress) {
        throw new Error('require --tokenAddress args')
      }

      if (!args.to) {
        throw new Error('require --to args')
      }

      const erc20 = BaseErc20Factory.connect(args.tokenAddress, wallet2)
      const tx = await erc20.approve(args.to, MaxUint256)
      console.log(tx)
      break
    }
    case 'allowanceOf': {
      if (!args.tokenAddress) {
        throw new Error('require --tokenAddress args')
      }

      if (!args.to) {
        throw new Error('require --to args')
      }

      const erc20 = BaseErc20Factory.connect(args.tokenAddress, wallet2)
      const tx = await erc20.allowance(wallet2.address, args.to)
      console.log(tx.toString())
      break
    }
    case 'balance': {
      if (!args.tokenAddress) {
        throw new Error('require --tokenAddress args')
      }

      const erc20 = BaseErc20Factory.connect(args.tokenAddress, wallet2)
      const tx = await erc20.balanceOf(wallet2.address)
      console.log(tx.toString())
      break
    }
    case 'mint': {
      const erc20 = BaseErc20Factory.connect(args.tokenAddress, wallet2)
      const tx = await erc20.mint(wallet2.address, 100000000000000)
      console.log(tx)
      break
    }
  }
}
Example #9
Source File: useApproveCallback.ts    From pancake-swap-exchange-testnet with GNU General Public License v3.0 5 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React()
  const token = amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined
  const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
  const pendingApproval = useHasPendingApproval(token?.address, spender)

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
    if (amountToApprove.currency === ETHER) return ApprovalState.APPROVED
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED
  }, [amountToApprove, currentAllowance, pendingApproval, spender])

  const tokenContract = useTokenContract(token?.address)
  const addTransaction = useTransactionAdder()

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error('approve was called unnecessarily')
      return
    }
    if (!token) {
      console.error('no token')
      return
    }

    if (!tokenContract) {
      console.error('tokenContract is null')
      return
    }

    if (!amountToApprove) {
      console.error('missing amount to approve')
      return
    }

    if (!spender) {
      console.error('no spender')
      return
    }

    let useExact = false
    const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
      // general fallback for tokens who restrict approval amounts
      useExact = true
      return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
    })

    // eslint-disable-next-line consistent-return
    return tokenContract
      .approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256, {
        gasLimit: calculateGasMargin(estimatedGas),
      })
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: `Approve ${amountToApprove.currency.symbol}`,
          approval: { tokenAddress: token.address, spender },
        })
      })
      .catch((error: Error) => {
        console.error('Failed to approve token', error)
        throw error
      })
  }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction])

  return [approvalState, approve]
}
Example #10
Source File: v1SwapArguments.ts    From goose-frontend-amm with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Get the arguments to make for a swap
 * @param trade trade to get v1 arguments for swapping
 * @param options options for swapping
 */
export default function v1SwapArguments(trade: Trade, options: Omit<TradeOptions, 'feeOnTransfer'>): SwapParameters {
  if (getTradeVersion(trade) !== Version.v1) {
    throw new Error('invalid trade version')
  }
  if (trade.route.pairs.length > 2) {
    throw new Error('too many pairs')
  }
  const isExactIn = trade.tradeType === TradeType.EXACT_INPUT
  const inputETH = trade.inputAmount.currency === ETHER
  const outputETH = trade.outputAmount.currency === ETHER
  if (inputETH && outputETH) throw new Error('ETHER to ETHER')
  const minimumAmountOut = toHex(trade.minimumAmountOut(options.allowedSlippage))
  const maximumAmountIn = toHex(trade.maximumAmountIn(options.allowedSlippage))
  const deadline = deadlineFromNow(options.ttl)
  if (isExactIn) {
    if (inputETH) {
      return {
        methodName: 'ethToTokenTransferInput',
        args: [minimumAmountOut, deadline, options.recipient],
        value: maximumAmountIn
      }
    } if (outputETH) {
      return {
        methodName: 'tokenToEthTransferInput',
        args: [maximumAmountIn, minimumAmountOut, deadline, options.recipient],
        value: '0x0'
      }
    } 
      const outputToken = trade.outputAmount.currency
      // should never happen, needed for type check
      if (!(outputToken instanceof Token)) {
        throw new Error('token to token')
      }
      return {
        methodName: 'tokenToTokenTransferInput',
        args: [maximumAmountIn, minimumAmountOut, '0x1', deadline, options.recipient, outputToken.address],
        value: '0x0'
      }
    
  } 
    if (inputETH) {
      return {
        methodName: 'ethToTokenTransferOutput',
        args: [minimumAmountOut, deadline, options.recipient],
        value: maximumAmountIn
      }
    } if (outputETH) {
      return {
        methodName: 'tokenToEthTransferOutput',
        args: [minimumAmountOut, maximumAmountIn, deadline, options.recipient],
        value: '0x0'
      }
    } 
      const output = trade.outputAmount.currency
      if (!(output instanceof Token)) {
        throw new Error('invalid output amount currency')
      }

      return {
        methodName: 'tokenToTokenTransferOutput',
        args: [
          minimumAmountOut,
          maximumAmountIn,
          MaxUint256.toHexString(),
          deadline,
          options.recipient,
          output.address
        ],
        value: '0x0'
      }
    
  
}
Example #11
Source File: useApproveCallback.ts    From goose-frontend-amm with GNU General Public License v3.0 5 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React()
  const token = amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined
  const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
  const pendingApproval = useHasPendingApproval(token?.address, spender)

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
    if (amountToApprove.currency === ETHER) return ApprovalState.APPROVED
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED
  }, [amountToApprove, currentAllowance, pendingApproval, spender])

  const tokenContract = useTokenContract(token?.address)
  const addTransaction = useTransactionAdder()

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error('approve was called unnecessarily')
      return
    }
    if (!token) {
      console.error('no token')
      return
    }

    if (!tokenContract) {
      console.error('tokenContract is null')
      return
    }

    if (!amountToApprove) {
      console.error('missing amount to approve')
      return
    }

    if (!spender) {
      console.error('no spender')
      return
    }

    let useExact = false
    const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
      // general fallback for tokens who restrict approval amounts
      useExact = true
      return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
    })

    // eslint-disable-next-line consistent-return
    return tokenContract
      .approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256, {
        gasLimit: calculateGasMargin(estimatedGas),
      })
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: `Approve ${amountToApprove.currency.symbol}`,
          approval: { tokenAddress: token.address, spender },
        })
      })
      .catch((error: Error) => {
        console.error('Failed to approve token', error)
        throw error
      })
  }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction])

  return [approvalState, approve]
}
Example #12
Source File: v1SwapArguments.ts    From cheeseswap-interface with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Get the arguments to make for a swap
 * @param trade trade to get v1 arguments for swapping
 * @param options options for swapping
 */
export default function v1SwapArguments(trade: Trade, options: Omit<TradeOptions, 'feeOnTransfer'>): SwapParameters {
  if (getTradeVersion(trade) !== Version.v1) {
    throw new Error('invalid trade version')
  }
  if (trade.route.pairs.length > 2) {
    throw new Error('too many pairs')
  }
  const isExactIn = trade.tradeType === TradeType.EXACT_INPUT
  const inputETH = trade.inputAmount.currency === ETHER
  const outputETH = trade.outputAmount.currency === ETHER
  if (inputETH && outputETH) throw new Error('ETHER to ETHER')
  const minimumAmountOut = toHex(trade.minimumAmountOut(options.allowedSlippage))
  const maximumAmountIn = toHex(trade.maximumAmountIn(options.allowedSlippage))
  const deadline = deadlineFromNow(options.ttl)
  if (isExactIn) {
    if (inputETH) {
      return {
        methodName: 'ethToTokenTransferInput',
        args: [minimumAmountOut, deadline, options.recipient],
        value: maximumAmountIn
      }
    } else if (outputETH) {
      return {
        methodName: 'tokenToEthTransferInput',
        args: [maximumAmountIn, minimumAmountOut, deadline, options.recipient],
        value: '0x0'
      }
    } else {
      const outputToken = trade.outputAmount.currency
      // should never happen, needed for type check
      if (!(outputToken instanceof Token)) {
        throw new Error('token to token')
      }
      return {
        methodName: 'tokenToTokenTransferInput',
        args: [maximumAmountIn, minimumAmountOut, '0x1', deadline, options.recipient, outputToken.address],
        value: '0x0'
      }
    }
  } else {
    if (inputETH) {
      return {
        methodName: 'ethToTokenTransferOutput',
        args: [minimumAmountOut, deadline, options.recipient],
        value: maximumAmountIn
      }
    } else if (outputETH) {
      return {
        methodName: 'tokenToEthTransferOutput',
        args: [minimumAmountOut, maximumAmountIn, deadline, options.recipient],
        value: '0x0'
      }
    } else {
      const output = trade.outputAmount.currency
      if (!(output instanceof Token)) {
        throw new Error('invalid output amount currency')
      }

      return {
        methodName: 'tokenToTokenTransferOutput',
        args: [
          minimumAmountOut,
          maximumAmountIn,
          MaxUint256.toHexString(),
          deadline,
          options.recipient,
          output.address
        ],
        value: '0x0'
      }
    }
  }
}
Example #13
Source File: useApproveCallback.ts    From cheeseswap-interface with GNU General Public License v3.0 5 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React()
  const token = amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined
  const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
  const pendingApproval = useHasPendingApproval(token?.address, spender)

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
    if (amountToApprove.currency === ETHER) return ApprovalState.APPROVED
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED
  }, [amountToApprove, currentAllowance, pendingApproval, spender])

  const tokenContract = useTokenContract(token?.address)
  const addTransaction = useTransactionAdder()

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error('approve was called unnecessarily')
      return
    }
    if (!token) {
      console.error('no token')
      return
    }

    if (!tokenContract) {
      console.error('tokenContract is null')
      return
    }

    if (!amountToApprove) {
      console.error('missing amount to approve')
      return
    }

    if (!spender) {
      console.error('no spender')
      return
    }

    let useExact = false
    const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
      // general fallback for tokens who restrict approval amounts
      useExact = true
      return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
    })

    return tokenContract
      .approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256, {
        gasLimit: calculateGasMargin(estimatedGas)
      })
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: 'Approve ' + amountToApprove.currency.symbol,
          approval: { tokenAddress: token.address, spender: spender }
        })
      })
      .catch((error: Error) => {
        console.debug('Failed to approve token', error)
        throw error
      })
  }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction])

  return [approvalState, approve]
}
Example #14
Source File: ByteConverters.ts    From casper-js-sdk with Apache License 2.0 5 votes vote down vote up
toBytesNumber = (bitSize: number, signed: boolean) => (
  value: BigNumberish
): Uint8Array => {
  const val = BigNumber.from(value);

  // Check bounds are safe for encoding
  const maxUintValue = MaxUint256.mask(bitSize);

  if (signed) {
    const bounds = maxUintValue.mask(bitSize - 1); // 1 bit for signed
    if (val.gt(bounds) || val.lt(bounds.add(One).mul(NegativeOne))) {
      throw new Error('value out-of-bounds, value: ' + value);
    }
  } else if (val.lt(Zero) || val.gt(maxUintValue.mask(bitSize))) {
    throw new Error('value out-of-bounds, value: ' + value);
  }

  const valTwos = val.toTwos(bitSize).mask(bitSize);

  const bytes = arrayify(valTwos);

  if (valTwos.gte(0)) {
    // for positive number, we had to deal with paddings
    if (bitSize > 64) {
      // if zero just return zero
      if (valTwos.eq(0)) {
        return bytes;
      }
      // for u128, u256, u512, we have to and append extra byte for length
      return concat([bytes, Uint8Array.from([bytes.length])])
        .slice()
        .reverse();
    } else {
      // for other types, we have to add padding 0s
      const byteLength = bitSize / 8;
      return concat([
        bytes.slice().reverse(),
        new Uint8Array(byteLength - bytes.length)
      ]);
    }
  } else {
    return bytes.reverse();
  }
}
Example #15
Source File: v1SwapArguments.ts    From cuiswap with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Get the arguments to make for a swap
 * @param trade trade to get v1 arguments for swapping
 * @param options options for swapping
 */
export default function v1SwapArguments(trade: Trade, options: Omit<TradeOptions, 'feeOnTransfer'>): SwapParameters {
  if (getTradeVersion(trade) !== Version.v1) {
    throw new Error('invalid trade version')
  }
  if (trade.route.pairs.length > 2) {
    throw new Error('too many pairs')
  }
  const isExactIn = trade.tradeType === TradeType.EXACT_INPUT
  const inputETH = trade.inputAmount.currency === ETHER
  const outputETH = trade.outputAmount.currency === ETHER
  if (inputETH && outputETH) throw new Error('ETHER to ETHER')
  const minimumAmountOut = toHex(trade.minimumAmountOut(options.allowedSlippage))
  const maximumAmountIn = toHex(trade.maximumAmountIn(options.allowedSlippage))
  const deadline = deadlineFromNow(options.ttl)
  if (isExactIn) {
    if (inputETH) {
      return {
        methodName: 'ethToTokenTransferInput',
        args: [minimumAmountOut, deadline, options.recipient],
        value: maximumAmountIn
      }
    } else if (outputETH) {
      return {
        methodName: 'tokenToEthTransferInput',
        args: [maximumAmountIn, minimumAmountOut, deadline, options.recipient],
        value: '0x0'
      }
    } else {
      const outputToken = trade.outputAmount.currency
      // should never happen, needed for type check
      if (!(outputToken instanceof Token)) {
        throw new Error('token to token')
      }
      return {
        methodName: 'tokenToTokenTransferInput',
        args: [maximumAmountIn, minimumAmountOut, '0x1', deadline, options.recipient, outputToken.address],
        value: '0x0'
      }
    }
  } else {
    if (inputETH) {
      return {
        methodName: 'ethToTokenTransferOutput',
        args: [minimumAmountOut, deadline, options.recipient],
        value: maximumAmountIn
      }
    } else if (outputETH) {
      return {
        methodName: 'tokenToEthTransferOutput',
        args: [minimumAmountOut, maximumAmountIn, deadline, options.recipient],
        value: '0x0'
      }
    } else {
      const output = trade.outputAmount.currency
      if (!(output instanceof Token)) {
        throw new Error('invalid output amount currency')
      }

      return {
        methodName: 'tokenToTokenTransferOutput',
        args: [
          minimumAmountOut,
          maximumAmountIn,
          MaxUint256.toHexString(),
          deadline,
          options.recipient,
          output.address
        ],
        value: '0x0'
      }
    }
  }
}
Example #16
Source File: useApproveCallback.ts    From cuiswap with GNU General Public License v3.0 5 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React()
  const token = amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined
  const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
  const pendingApproval = useHasPendingApproval(token?.address, spender)

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
    if (amountToApprove.currency === ETHER) return ApprovalState.APPROVED
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED
  }, [amountToApprove, currentAllowance, pendingApproval, spender])

  const tokenContract = useTokenContract(token?.address)
  const addTransaction = useTransactionAdder()

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error('approve was called unnecessarily')
      return
    }
    if (!token) {
      console.error('no token')
      return
    }

    if (!tokenContract) {
      console.error('tokenContract is null')
      return
    }

    if (!amountToApprove) {
      console.error('missing amount to approve')
      return
    }

    if (!spender) {
      console.error('no spender')
      return
    }

    let useExact = false
    const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
      // general fallback for tokens who restrict approval amounts
      useExact = true
      return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
    })

    return tokenContract
      .approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256, {
        gasLimit: calculateGasMargin(estimatedGas)
      })
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: 'Approve ' + amountToApprove.currency.symbol,
          approval: { tokenAddress: token.address, spender: spender }
        })
      })
      .catch((error: Error) => {
        console.debug('Failed to approve token', error)
        throw error
      })
  }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction])

  return [approvalState, approve]
}
Example #17
Source File: useApproveCallback.ts    From glide-frontend with GNU General Public License v3.0 4 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string,
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React()
  const token = amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined
  const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
  const pendingApproval = useHasPendingApproval(token?.address, spender)

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
    if (amountToApprove.currency === ETHER) return ApprovalState.APPROVED
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED
  }, [amountToApprove, currentAllowance, pendingApproval, spender])

  const tokenContract = useTokenContract(token?.address)
  const addTransaction = useTransactionAdder()

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error('approve was called unnecessarily')
      return
    }
    if (!token) {
      console.error('no token')
      return
    }

    if (!tokenContract) {
      console.error('tokenContract is null')
      return
    }

    if (!amountToApprove) {
      console.error('missing amount to approve')
      return
    }

    if (!spender) {
      console.error('no spender')
      return
    }

    let useExact = false

    const isApproveBalanceToken = APPROVE_BALANCE_ADDRESSES.includes(token?.address)
    useExact = isApproveBalanceToken;

    // const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
    //   // general fallback for tokens who restrict approval amounts
    //   useExact = true
    //   return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
    // })

    const estimatedGas = useExact ? await tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString()) : await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
      // general fallback for tokens who restrict approval amounts
      useExact = true
      return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
    })

    // eslint-disable-next-line consistent-return
    return tokenContract
      .approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256, {
        gasLimit: calculateGasMargin(estimatedGas),
      })
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: `Approve ${amountToApprove.currency.symbol}`,
          approval: { tokenAddress: token.address, spender },
        })
      })
      .catch((error: Error) => {
        console.error('Failed to approve token', error)
        throw error
      })
  }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction])

  return [approvalState, approve]
}
Example #18
Source File: useApproveCallback.ts    From limit-orders-lib with GNU General Public License v3.0 4 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount<Currency>,
  spender?: string
): [ApprovalState, () => Promise<void>] {
  const { account } = useWeb3();
  const token = amountToApprove?.currency?.isToken
    ? amountToApprove.currency
    : undefined;
  const currentAllowance = useTokenAllowance(
    token,
    account ?? undefined,
    spender
  );
  const pendingApproval = useHasPendingApproval(token?.address, spender);

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN;
    if (amountToApprove.currency.isNative) return ApprovalState.APPROVED;
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN;

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED;
  }, [amountToApprove, currentAllowance, pendingApproval, spender]);

  const tokenContract = useTokenContract(token?.address);
  const addTransaction = useTransactionAdder();

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error("approve was called unnecessarily");
      return;
    }
    if (!token) {
      console.error("no token");
      return;
    }

    if (!tokenContract) {
      console.error("tokenContract is null");
      return;
    }

    if (!amountToApprove) {
      console.error("missing amount to approve");
      return;
    }

    if (!spender) {
      console.error("no spender");
      return;
    }

    let useExact = false;
    const estimatedGas = await tokenContract.estimateGas
      .approve(spender, MaxUint256)
      .catch(() => {
        // general fallback for tokens who restrict approval amounts
        useExact = true;
        return tokenContract.estimateGas.approve(
          spender,
          amountToApprove.quotient.toString()
        );
      });

    return tokenContract
      .approve(
        spender,
        useExact ? amountToApprove.quotient.toString() : MaxUint256,
        {
          gasLimit: calculateGasMargin(estimatedGas),
        }
      )
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: "Approve " + amountToApprove.currency.symbol,
          type: "approval",
          approval: { tokenAddress: token.address, spender: spender },
        });
      })
      .catch((error: Error) => {
        console.debug("Failed to approve token", error);
        throw error;
      });
  }, [
    approvalState,
    token,
    tokenContract,
    amountToApprove,
    spender,
    addTransaction,
  ]);

  return [approvalState, approve];
}
Example #19
Source File: v1SwapArguments.ts    From dyp with Do What The F*ck You Want To Public License 4 votes vote down vote up
/**
 * Get the arguments to make for a swap
 * @param trade trade to get v1 arguments for swapping
 * @param options options for swapping
 */
export default function v1SwapArguments(
  trade: Trade,
  options: Omit<TradeOptionsDeadline, 'feeOnTransfer'>
): SwapParameters {
  if (getTradeVersion(trade) !== Version.v1) {
    throw new Error('invalid trade version')
  }
  if (trade.route.pairs.length > 2) {
    throw new Error('too many pairs')
  }
  const isExactIn = trade.tradeType === TradeType.EXACT_INPUT
  const inputETH = trade.inputAmount.currency === ETHER
  const outputETH = trade.outputAmount.currency === ETHER
  if (inputETH && outputETH) throw new Error('ETHER to ETHER')
  const minimumAmountOut = toHex(trade.minimumAmountOut(options.allowedSlippage))
  const maximumAmountIn = toHex(trade.maximumAmountIn(options.allowedSlippage))
  const deadline = `0x${options.deadline.toString(16)}`
  if (isExactIn) {
    if (inputETH) {
      return {
        methodName: 'ethToTokenTransferInput',
        args: [minimumAmountOut, deadline, options.recipient],
        value: maximumAmountIn
      }
    } else if (outputETH) {
      return {
        methodName: 'tokenToEthTransferInput',
        args: [maximumAmountIn, minimumAmountOut, deadline, options.recipient],
        value: '0x0'
      }
    } else {
      const outputToken = trade.outputAmount.currency
      // should never happen, needed for type check
      if (!(outputToken instanceof Token)) {
        throw new Error('token to token')
      }
      return {
        methodName: 'tokenToTokenTransferInput',
        args: [maximumAmountIn, minimumAmountOut, '0x1', deadline, options.recipient, outputToken.address],
        value: '0x0'
      }
    }
  } else {
    if (inputETH) {
      return {
        methodName: 'ethToTokenTransferOutput',
        args: [minimumAmountOut, deadline, options.recipient],
        value: maximumAmountIn
      }
    } else if (outputETH) {
      return {
        methodName: 'tokenToEthTransferOutput',
        args: [minimumAmountOut, maximumAmountIn, deadline, options.recipient],
        value: '0x0'
      }
    } else {
      const output = trade.outputAmount.currency
      if (!(output instanceof Token)) {
        throw new Error('invalid output amount currency')
      }

      return {
        methodName: 'tokenToTokenTransferOutput',
        args: [
          minimumAmountOut,
          maximumAmountIn,
          MaxUint256.toHexString(),
          deadline,
          options.recipient,
          output.address
        ],
        value: '0x0'
      }
    }
  }
}
Example #20
Source File: Market.test.ts    From core with GNU General Public License v3.0 4 votes vote down vote up
describe('Market', () => {
  let [
    deployerWallet,
    bidderWallet,
    mockTokenWallet,
    otherWallet,
  ] = generatedWallets(provider);

  let defaultBidShares = {
    prevOwner: Decimal.new(10),
    owner: Decimal.new(80),
    creator: Decimal.new(10),
  };

  let defaultTokenId = 1;
  let defaultAsk = {
    amount: 100,
    currency: '0x41A322b28D0fF354040e2CbC676F0320d8c8850d',
    sellOnShare: Decimal.new(0),
  };

  let auctionAddress: string;

  function toNumWei(val: BigNumber) {
    return parseFloat(formatUnits(val, 'wei'));
  }

  function toNumEther(val: BigNumber) {
    return parseFloat(formatUnits(val, 'ether'));
  }

  async function auctionAs(wallet: Wallet) {
    return MarketFactory.connect(auctionAddress, wallet);
  }
  async function deploy() {
    const auction = await (
      await new MarketFactory(deployerWallet).deploy()
    ).deployed();
    auctionAddress = auction.address;
  }
  async function configure() {
    return MarketFactory.connect(auctionAddress, deployerWallet).configure(
      mockTokenWallet.address
    );
  }

  async function readMediaContract() {
    return MarketFactory.connect(
      auctionAddress,
      deployerWallet
    ).mediaContract();
  }

  async function setBidShares(
    auction: Market,
    tokenId: number,
    bidShares?: BidShares
  ) {
    return auction.setBidShares(tokenId, bidShares);
  }

  async function setAsk(auction: Market, tokenId: number, ask?: Ask) {
    return auction.setAsk(tokenId, ask);
  }

  async function deployCurrency() {
    const currency = await new BaseErc20Factory(deployerWallet).deploy(
      'test',
      'TEST',
      18
    );
    return currency.address;
  }

  async function mintCurrency(currency: string, to: string, value: number) {
    await BaseErc20Factory.connect(currency, deployerWallet).mint(to, value);
  }

  async function approveCurrency(
    currency: string,
    spender: string,
    owner: Wallet
  ) {
    await BaseErc20Factory.connect(currency, owner).approve(
      spender,
      MaxUint256
    );
  }
  async function getBalance(currency: string, owner: string) {
    return BaseErc20Factory.connect(currency, deployerWallet).balanceOf(owner);
  }
  async function setBid(
    auction: Market,
    bid: Bid,
    tokenId: number,
    spender?: string
  ) {
    await auction.setBid(tokenId, bid, spender || bid.bidder);
  }

  beforeEach(async () => {
    await blockchain.resetAsync();
  });

  describe('#constructor', () => {
    it('should be able to deploy', async () => {
      await expect(deploy()).eventually.fulfilled;
    });
  });

  describe('#configure', () => {
    beforeEach(async () => {
      await deploy();
    });

    it('should revert if not called by the owner', async () => {
      await expect(
        MarketFactory.connect(auctionAddress, otherWallet).configure(
          mockTokenWallet.address
        )
      ).eventually.rejectedWith('Market: Only owner');
    });

    it('should be callable by the owner', async () => {
      await expect(configure()).eventually.fulfilled;
      const tokenContractAddress = await readMediaContract();

      expect(tokenContractAddress).eq(mockTokenWallet.address);
    });

    it('should reject if called twice', async () => {
      await configure();

      await expect(configure()).eventually.rejectedWith(
        'Market: Already configured'
      );
    });
  });

  describe('#setBidShares', () => {
    beforeEach(async () => {
      await deploy();
      await configure();
    });

    it('should reject if not called by the media address', async () => {
      const auction = await auctionAs(otherWallet);

      await expect(
        setBidShares(auction, defaultTokenId, defaultBidShares)
      ).rejectedWith('Market: Only media contract');
    });

    it('should set the bid shares if called by the media address', async () => {
      const auction = await auctionAs(mockTokenWallet);

      await expect(setBidShares(auction, defaultTokenId, defaultBidShares))
        .eventually.fulfilled;

      const tokenBidShares = Object.values(
        await auction.bidSharesForToken(defaultTokenId)
      ).map((s) => parseInt(formatUnits(s.value, 'ether')));

      expect(tokenBidShares[0]).eq(
        toNumEther(defaultBidShares.prevOwner.value)
      );
      expect(tokenBidShares[1]).eq(toNumEther(defaultBidShares.creator.value));
      expect(tokenBidShares[2]).eq(toNumEther(defaultBidShares.owner.value));
    });

    it('should emit an event when bid shares are updated', async () => {
      const auction = await auctionAs(mockTokenWallet);

      const block = await provider.getBlockNumber();
      await setBidShares(auction, defaultTokenId, defaultBidShares);
      const events = await auction.queryFilter(
        auction.filters.BidShareUpdated(null, null),
        block
      );
      expect(events.length).eq(1);
      const logDescription = auction.interface.parseLog(events[0]);
      expect(toNumWei(logDescription.args.tokenId)).to.eq(defaultTokenId);
      expect(toNumWei(logDescription.args.bidShares.prevOwner.value)).to.eq(
        toNumWei(defaultBidShares.prevOwner.value)
      );
      expect(toNumWei(logDescription.args.bidShares.creator.value)).to.eq(
        toNumWei(defaultBidShares.creator.value)
      );
      expect(toNumWei(logDescription.args.bidShares.owner.value)).to.eq(
        toNumWei(defaultBidShares.owner.value)
      );
    });

    it('should reject if the bid shares are invalid', async () => {
      const auction = await auctionAs(mockTokenWallet);
      const invalidBidShares = {
        prevOwner: Decimal.new(0),
        owner: Decimal.new(0),
        creator: Decimal.new(101),
      };

      await expect(
        setBidShares(auction, defaultTokenId, invalidBidShares)
      ).rejectedWith('Market: Invalid bid shares, must sum to 100');
    });
  });

  describe('#setAsk', () => {
    beforeEach(async () => {
      await deploy();
      await configure();
    });

    it('should reject if not called by the media address', async () => {
      const auction = await auctionAs(otherWallet);

      await expect(setAsk(auction, defaultTokenId, defaultAsk)).rejectedWith(
        'Market: Only media contract'
      );
    });

    it('should set the ask if called by the media address', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await setBidShares(auction, defaultTokenId, defaultBidShares);

      await expect(setAsk(auction, defaultTokenId, defaultAsk)).eventually
        .fulfilled;

      const ask = await auction.currentAskForToken(defaultTokenId);

      expect(toNumWei(ask.amount)).to.eq(defaultAsk.amount);
      expect(ask.currency).to.eq(defaultAsk.currency);
    });

    it('should emit an event if the ask is updated', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await setBidShares(auction, defaultTokenId, defaultBidShares);

      const block = await provider.getBlockNumber();
      await setAsk(auction, defaultTokenId, defaultAsk);
      const events = await auction.queryFilter(
        auction.filters.AskCreated(null, null),
        block
      );

      expect(events.length).eq(1);
      const logDescription = auction.interface.parseLog(events[0]);
      expect(toNumWei(logDescription.args.tokenId)).to.eq(defaultTokenId);
      expect(toNumWei(logDescription.args.ask.amount)).to.eq(defaultAsk.amount);
      expect(logDescription.args.ask.currency).to.eq(defaultAsk.currency);
    });

    it('should reject if the ask is too low', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await setBidShares(auction, defaultTokenId, defaultBidShares);

      await expect(
        setAsk(auction, defaultTokenId, {
          amount: 1,
          currency: AddressZero,
        })
      ).rejectedWith('Market: Ask invalid for share splitting');
    });

    it("should reject if the bid shares haven't been set yet", async () => {
      const auction = await auctionAs(mockTokenWallet);
      await expect(setAsk(auction, defaultTokenId, defaultAsk)).rejectedWith(
        'Market: Invalid bid shares for token'
      );
    });
  });

  describe('#setBid', () => {
    let currency: string;
    const defaultBid = {
      amount: 100,
      currency: currency,
      bidder: bidderWallet.address,
      recipient: otherWallet.address,
      spender: bidderWallet.address,
      sellOnShare: Decimal.new(10),
    };

    beforeEach(async () => {
      await deploy();
      await configure();
      currency = await deployCurrency();
      defaultBid.currency = currency;
    });

    it('should revert if not called by the media contract', async () => {
      const auction = await auctionAs(otherWallet);
      await expect(setBid(auction, defaultBid, defaultTokenId)).rejectedWith(
        'Market: Only media contract'
      );
    });

    it('should revert if the bidder does not have a high enough allowance for their bidding currency', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await expect(setBid(auction, defaultBid, defaultTokenId)).rejectedWith(
        'SafeERC20: ERC20 operation did not succeed'
      );
    });

    it('should revert if the bidder does not have enough tokens to bid with', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await mintCurrency(currency, defaultBid.bidder, defaultBid.amount - 1);
      await approveCurrency(currency, auction.address, bidderWallet);

      await expect(setBid(auction, defaultBid, defaultTokenId)).rejectedWith(
        'SafeERC20: ERC20 operation did not succeed'
      );
    });

    it('should revert if the bid currency is 0 address', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await setBidShares(auction, defaultTokenId, defaultBidShares);
      await mintCurrency(currency, defaultBid.bidder, defaultBid.amount);
      await approveCurrency(currency, auction.address, bidderWallet);

      await expect(
        setBid(
          auction,
          { ...defaultBid, currency: AddressZero },
          defaultTokenId
        )
      ).rejectedWith('Market: bid currency cannot be 0 address');
    });

    it('should revert if the bid recipient is 0 address', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await setBidShares(auction, defaultTokenId, defaultBidShares);
      await mintCurrency(currency, defaultBid.bidder, defaultBid.amount);
      await approveCurrency(currency, auction.address, bidderWallet);

      await expect(
        setBid(
          auction,
          { ...defaultBid, recipient: AddressZero },
          defaultTokenId
        )
      ).rejectedWith('Market: bid recipient cannot be 0 address');
    });

    it('should revert if the bidder bids 0 tokens', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await setBidShares(auction, defaultTokenId, defaultBidShares);
      await mintCurrency(currency, defaultBid.bidder, defaultBid.amount);
      await approveCurrency(currency, auction.address, bidderWallet);

      await expect(
        setBid(auction, { ...defaultBid, amount: 0 }, defaultTokenId)
      ).rejectedWith('Market: cannot bid amount of 0');
    });

    it('should accept a valid bid', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await setBidShares(auction, defaultTokenId, defaultBidShares);
      await mintCurrency(currency, defaultBid.bidder, defaultBid.amount);
      await approveCurrency(currency, auction.address, bidderWallet);

      const beforeBalance = toNumWei(
        await getBalance(currency, defaultBid.bidder)
      );

      await expect(setBid(auction, defaultBid, defaultTokenId)).fulfilled;

      const afterBalance = toNumWei(
        await getBalance(currency, defaultBid.bidder)
      );
      const bid = await auction.bidForTokenBidder(1, bidderWallet.address);
      expect(bid.currency).eq(currency);
      expect(toNumWei(bid.amount)).eq(defaultBid.amount);
      expect(bid.bidder).eq(defaultBid.bidder);
      expect(beforeBalance).eq(afterBalance + defaultBid.amount);
    });

    it('should accept a valid bid larger than the min bid', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await setBidShares(auction, defaultTokenId, defaultBidShares);

      const largerValidBid = {
        amount: 130000000,
        currency: currency,
        bidder: bidderWallet.address,
        recipient: otherWallet.address,
        spender: bidderWallet.address,
        sellOnShare: Decimal.new(10),
      };

      await mintCurrency(
        currency,
        largerValidBid.bidder,
        largerValidBid.amount
      );
      await approveCurrency(currency, auction.address, bidderWallet);

      const beforeBalance = toNumWei(
        await getBalance(currency, defaultBid.bidder)
      );

      await expect(setBid(auction, largerValidBid, defaultTokenId)).fulfilled;

      const afterBalance = toNumWei(
        await getBalance(currency, largerValidBid.bidder)
      );
      const bid = await auction.bidForTokenBidder(1, bidderWallet.address);
      expect(bid.currency).eq(currency);
      expect(toNumWei(bid.amount)).eq(largerValidBid.amount);
      expect(bid.bidder).eq(largerValidBid.bidder);
      expect(beforeBalance).eq(afterBalance + largerValidBid.amount);
    });

    it('should refund the original bid if the bidder bids again', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await setBidShares(auction, defaultTokenId, defaultBidShares);
      await mintCurrency(currency, defaultBid.bidder, 5000);
      await approveCurrency(currency, auction.address, bidderWallet);

      const bidderBalance = toNumWei(
        await BaseErc20Factory.connect(currency, bidderWallet).balanceOf(
          bidderWallet.address
        )
      );

      await setBid(auction, defaultBid, defaultTokenId);
      await expect(
        setBid(
          auction,
          { ...defaultBid, amount: defaultBid.amount * 2 },
          defaultTokenId
        )
      ).fulfilled;

      const afterBalance = toNumWei(
        await BaseErc20Factory.connect(currency, bidderWallet).balanceOf(
          bidderWallet.address
        )
      );
      await expect(afterBalance).eq(bidderBalance - defaultBid.amount * 2);
    });

    it('should emit a bid event', async () => {
      const auction = await auctionAs(mockTokenWallet);
      await setBidShares(auction, defaultTokenId, defaultBidShares);
      await mintCurrency(currency, defaultBid.bidder, 5000);
      await approveCurrency(currency, auction.address, bidderWallet);

      const block = await provider.getBlockNumber();
      await setBid(auction, defaultBid, defaultTokenId);
      const events = await auction.queryFilter(
        auction.filters.BidCreated(null, null),
        block
      );

      expect(events.length).eq(1);
      const logDescription = auction.interface.parseLog(events[0]);
      expect(toNumWei(logDescription.args.tokenId)).to.eq(defaultTokenId);
      expect(toNumWei(logDescription.args.bid.amount)).to.eq(defaultBid.amount);
      expect(logDescription.args.bid.currency).to.eq(defaultBid.currency);
      expect(toNumWei(logDescription.args.bid.sellOnShare.value)).to.eq(
        toNumWei(defaultBid.sellOnShare.value)
      );
    });
  });
});
Example #21
Source File: Arbitrageur.ts    From sakeperp-arbitrageur with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
async arbitrageExchange(exchange: Exchange, systemMetadata: EthMetadata): Promise<void> {
        const exchangeState = await this.perpService.getExchangeStates(exchange.address)
        const exchangePair = this.getExchangePair(exchangeState)
        const exchangeConfig = this.exchangeConfigMap[exchangePair]

        if (!exchangeConfig) {
            return
        }

        if (!exchangeConfig.ENABLED) {
            return
        }

        this.log.jinfo({
            event: "ArbitrageExchange",
            params: {
                exchangePair,
                exchangeConfig,
            },
        })

        const arbitrageurAddr = this.arbitrageur.address
        const sakePerpAddr = systemMetadata.sakePerpAddr
        const quoteAssetAddr = await exchange.quoteAsset()

        // Check balance - quote asset is BUSD
        const quoteBalance = await this.checkDexBalance(quoteAssetAddr, arbitrageurAddr)

        this.sakeperpBalance = quoteBalance

        // Make sure the quote asset are approved
        const allowance = await this.erc20Service.allowance(quoteAssetAddr, arbitrageurAddr, sakePerpAddr)
        const infiniteAllowance = await this.erc20Service.fromScaled(quoteAssetAddr, MaxUint256)
        const allowanceThreshold = infiniteAllowance.div(2)
        if (allowance.lt(allowanceThreshold)) {
            await this.erc20Service.approve(quoteAssetAddr, sakePerpAddr, infiniteAllowance, this.arbitrageur, {
                gasPrice: await this.ethService.getSafeGasPrice(),
            })
            this.log.jinfo({
                event: "SetMaxAllowance",
                params: {
                    quoteAssetAddr: quoteAssetAddr,
                    owner: this.arbitrageur.address,
                    agent: sakePerpAddr,
                },
            })
        }

        // List Perpetual Protocol positions
        const [position, unrealizedPnl] = await Promise.all([
            this.perpService.getPersonalPositionWithFundingPayment(exchange.address, this.arbitrageur.address),
            this.perpService.getUnrealizedPnl(exchange.address, this.arbitrageur.address, PnlCalcOption.SPOT_PRICE),
        ])


        this.log.jinfo({
            event: "SakePerpPosition",
            params: {
                exchangePair,
                size: +position.size.toFixed(6),
                margin: +position.margin.toFixed(6),
                openNotional: +position.openNotional.toFixed(6),
                unrealizedPnl: +unrealizedPnl.toFixed(6),
                quoteBalance: +quoteBalance.toFixed(6),
                accountValue: +position.margin.add(unrealizedPnl).add(quoteBalance).toFixed(6),
            },
        })

        // List CEX positions
        const cexPosition = await this.cexService.getPosition(this.cexClient, exchangeConfig.CEX_MARKET_ID)
        if (cexPosition) {
            const cexSizeDiff = cexPosition.netSize.abs().sub(position.size.abs())
            this.log.jinfo({
                event: "CexPosition",
                params: {
                    marketId: cexPosition.future,
                    size: +cexPosition.netSize.toFixed(6),
                    diff: +cexSizeDiff.toFixed(6),
                },
            })

            if (cexSizeDiff.abs().gte(exchangeConfig.CEX_MIN_TRADE_SIZE)) {
                const mitigation = mitigatePositionSizeDiff(position.size, cexPosition.netSize)
                this.log.jinfo({
                    event: "MitigateCEXPositionSizeDiff",
                    params: {
                        sakeperpPositionSize: position.size,
                        cexPositionSize: cexPosition.netSize,
                        size: mitigation.sizeAbs,
                        side: mitigation.side,
                    },
                })
                await this.openCEXPosition(exchangeConfig.CEX_MARKET_ID, exchangeConfig.SAKEPERP_LEVERAGE, mitigation.sizeAbs, mitigation.side, true)
            }
        }

        // Adjust Perpetual Protocol margin
        if (!position.size.eq(0)) {
            const marginRatio = await this.perpService.getMarginRatio(exchange.address, arbitrageurAddr)
            // note positionNotional is an estimation because the real margin ratio is calculated based on two mark price candidates: SPOT & TWAP.
            // we pick the more "conservative" one (SPOT) here so the margin required tends to fall on the safer side
            const {
                positionNotional: spotPositionNotional,
            } = await this.perpService.getPositionNotionalAndUnrealizedPnl(
                exchange.address,
                this.arbitrageur.address,
                PnlCalcOption.SPOT_PRICE,
            )
            this.log.jinfo({
                event: "MarginRatioBefore",
                params: {
                    marginRatio: +marginRatio.toFixed(6),
                    spotPositionNotional: +spotPositionNotional.toFixed(6),
                    exchangePair,
                },
            })
            const expectedMarginRatio = new Big(1).div(exchangeConfig.SAKEPERP_LEVERAGE)
            if (marginRatio.gt(expectedMarginRatio.mul(new Big(1).add(exchangeConfig.ADJUST_MARGIN_RATIO_THRESHOLD)))) {
                // marginToBeRemoved = -marginToChange
                //                   = (marginRatio - expectedMarginRatio) * positionNotional
                let marginToBeRemoved = marginRatio.sub(expectedMarginRatio).mul(spotPositionNotional)

                // cap the reduction by the current (funding payment realized) margin
                if (marginToBeRemoved.gt(position.margin)) {
                    marginToBeRemoved = position.margin
                }
                if (marginToBeRemoved.gt(Big(0))) {
                    this.log.jinfo({
                        event: "RemoveMargin",
                        params: {
                            exchangePair,
                            marginToBeRemoved: +marginToBeRemoved,
                        },
                    })

                    const release = await this.nonceMutex.acquire()
                    let tx
                    try {
                        tx = await this.perpService.removeMargin(this.arbitrageur, exchange.address, marginToBeRemoved, {
                            nonce: this.nextNonce,
                            gasPrice: await this.ethService.getSafeGasPrice(),
                        })
                        this.nextNonce++
                    } finally {
                        release()
                    }
                    await tx.wait()
                    this.log.jinfo({
                        event: "MarginRatioAfter",
                        params: {
                            exchangePair,
                            marginRatio: (
                                await this.perpService.getMarginRatio(exchange.address, arbitrageurAddr)
                            ).toFixed(),
                        },
                    })
                }
            } else if (
                marginRatio.lt(expectedMarginRatio.mul(new Big(1).sub(exchangeConfig.ADJUST_MARGIN_RATIO_THRESHOLD)))
            ) {
                // marginToBeAdded = marginToChange
                //                 = (expectedMarginRatio - marginRatio) * positionNotional
                let marginToBeAdded = expectedMarginRatio.sub(marginRatio).mul(spotPositionNotional)
                marginToBeAdded = marginToBeAdded.gt(quoteBalance) ? quoteBalance : marginToBeAdded
                this.log.jinfo({
                    event: "AddMargin",
                    params: {
                        exchangePair,
                        marginToBeAdded: marginToBeAdded.toFixed(),
                    },
                })

                const release = await this.nonceMutex.acquire()
                let tx
                try {
                    tx = await this.perpService.addMargin(this.arbitrageur, exchange.address, marginToBeAdded, {
                        nonce: this.nextNonce,
                        gasPrice: await this.ethService.getSafeGasPrice(),
                    })
                    this.nextNonce++
                } finally {
                    release()
                }
                await tx.wait()
                this.log.jinfo({
                    event: "MarginRatioAfter",
                    params: {
                        exchangePair,
                        marginRatio: (await this.perpService.getMarginRatio(exchange.address, arbitrageurAddr)).toFixed(),
                    },
                })
            }
        }

        // NOTE If the arbitrageur is already out of balance,
        // we will leave it as is and not do any rebalance work

        // Fetch prices
        const [exchangePrice, cexPrice] = await Promise.all([
            this.fetchExchangePrice(exchange),
            this.fetchCexPrice(exchangeConfig),
            // this.fetchOraclePrice(exchange),
        ])

        // Calculate spread
        // NOTE We assume CEX liquidity is always larger than Perpetual Protocol,
        // so we use Perpetual Protocol to calculate the max slippage
        const spread = exchangePrice.sub(cexPrice).div(cexPrice)
        const amount = Arbitrageur.calcMaxSlippageAmount(
            exchangePrice,
            exchangeConfig.MAX_SLIPPAGE_RATIO,
            exchangeState.baseAssetReserve,
            exchangeState.quoteAssetReserve,
        )

        if (!position.size.eq(0)) { 
            const check = await this.checkCexPositionRisk(exchangeConfig.CEX_MARKET_ID,spread,exchange,cexPosition)
            if (!check){
                return
            }
            const gapAmm  = await this.perpService.getGapForMovingAmm(exchange.address, systemMetadata.sakePerpVault)
 

            let op = ""         
            const openPrice = position.openNotional.div(position.size.abs())  

            if (gapAmm.gt(0)) {
                const priceDiff = exchangePrice.sub(openPrice).div(openPrice)    
                if (position.size.gt(0)) { // long
                    if ( spread.gte(Big(exchangeConfig.SAKEPERP_LONG_CLOSE_TRIGGER)) && cexPrice.lt(openPrice)) {
                        op = "long_loss"
                    } 
                    if ( spread.gte(Big(exchangeConfig.SAKEPERP_LONG_CLOSE_TRIGGER)) && priceDiff.gt(Big(exchangeConfig.SAKEPERP_LONG_OPEN_PRICE_SPREAD))) {
                        op = "long_profit" 
                    }
                } else {  // short
                    if ( spread.lte(Big(exchangeConfig.SAKEPERP_SHORT_CLOSE_TRIGGER)) && (cexPrice.gt(openPrice))) {
                        op = "short_loss"
                    }
                    if ( spread.lte(Big(exchangeConfig.SAKEPERP_SHORT_CLOSE_TRIGGER)) && priceDiff.lt(Big(exchangeConfig.SAKEPERP_SHORT_OPEN_PRICE_SPREAD))) {
                        op = "short_profit" 
                    } 
                }

                this.log.jinfo({
                    event: "PositionProfit",
                    params: {
                        exchangePair,
                        operate: op,
                        position:  +position.size.toFixed(6),
                        openNotional: +position.openNotional.toFixed(6), 
                        openPrice: +openPrice.toFixed(6),
                        ammPrice: +exchangePrice.toFixed(6),
                        cexPrice: +cexPrice.toFixed(6),
                        amm_cex: +spread.toFixed(6),
                        amm_open: +priceDiff.toFixed(6),
                        gapAmm: +gapAmm.toFixed(6),
                    },
                })
    
            }else{
                const priceDiff = cexPrice.sub(openPrice).div(openPrice)    
                if (position.size.gt(0)) { // long
                    if ( priceDiff.gte(exchangeConfig.SAKEPERP_LONG_CEX_OPEN_PRICE_SPREAD)) {
                        op = "long_stop" 
                    }
                } else {  // short
                    if ( priceDiff.lte(exchangeConfig.SAKEPERP_SHORT_CEX_OPEN_PRICE_SPREAD)) {
                        op = "short_stop" 
                    } 
                } 

                this.log.jinfo({
                    event: "PositionProfit",
                    params: {
                        exchangePair,
                        operate: op,
                        position:  +position.size.toFixed(6),
                        openNotional: +position.openNotional.toFixed(6), 
                        openPrice: +openPrice.toFixed(6),
                        ammPrice: +exchangePrice.toFixed(6),
                        cexPrice: +cexPrice.toFixed(6),
                        amm_cex: +spread.toFixed(6),
                        cex_open: +priceDiff.toFixed(6),
                        gapAmm: +gapAmm.toFixed(6), 
                    },
                })    
            }
           

            if (op != "") {
                await Promise.all([
                    this.closeSakePerpPosition(exchange, exchangePair),
                    this.closeCexPosition(exchangeConfig, cexPosition)
                ])
                this.setTradingData(exchangeConfig.CEX_MARKET_ID, Big(0))
            }
        }else{

            const gapAmm  = await this.perpService.getGapForMovingAmm(exchange.address, systemMetadata.sakePerpVault)

            this.log.jinfo({
                event: "CalculatedPrice",
                params: {
                    exchangePair,
                    ammPrice: exchangePrice.toFixed(4),
                    cexPrice: cexPrice.toFixed(4),
                    amm_cex: spread.toFixed(4),
                    gapAmm: +gapAmm.toFixed(6),
                },
            })

            if (gapAmm.lte(0)) {
                return
            }

            // Open positions if needed
            if (spread.lte(exchangeConfig.SAKEPERP_LONG_ENTRY_TRIGGER)) {
                const result = await this.perpService.checkWaitingPeriod(this.arbitrageur, exchange.address, this.arbitrageur.address, Side.BUY)
                if (!result){
                    return
                }

                const regAmount = this.calculateRegulatedPositionNotional(exchangePair, exchangeConfig, quoteBalance, amount, position, Side.BUY)
                const cexPositionSizeAbs = this.calculateCEXPositionSize(exchangeConfig, regAmount, cexPrice)
                if (cexPositionSizeAbs.eq(Big(0))) {
                    return
                }

                await Promise.all([
                    this.openCEXPosition(exchangeConfig.CEX_MARKET_ID, exchangeConfig.SAKEPERP_LEVERAGE, cexPositionSizeAbs, Side.SELL, true),
                    this.openSakePerpPosition(exchange, exchangePair, regAmount, exchangeConfig.SAKEPERP_LEVERAGE, Side.BUY),
                ])
                this.setTradingData(exchangeConfig.CEX_MARKET_ID, spread)
 
            } else if (spread.gte(exchangeConfig.SAKEPERP_SHORT_ENTRY_TRIGGER)) {
                const result = await this.perpService.checkWaitingPeriod(this.arbitrageur, exchange.address, this.arbitrageur.address, Side.SELL)
                if (!result){
                    return
                }

                const regAmount = this.calculateRegulatedPositionNotional(exchangePair, exchangeConfig, quoteBalance, amount, position, Side.SELL)
                const cexPositionSizeAbs = this.calculateCEXPositionSize(exchangeConfig, regAmount, cexPrice)
                if (cexPositionSizeAbs.eq(Big(0))) {
                    return
                }

                await Promise.all([
                    this.openCEXPosition(exchangeConfig.CEX_MARKET_ID, exchangeConfig.SAKEPERP_LEVERAGE, cexPositionSizeAbs, Side.BUY, true),
                    this.openSakePerpPosition(exchange, exchangePair, regAmount, exchangeConfig.SAKEPERP_LEVERAGE, Side.SELL),
                ])

                this.setTradingData(exchangeConfig.CEX_MARKET_ID, spread)

            } else {
                this.log.jinfo({
                    event: "NotTriggered",
                    params: {
                        exchangePair,
                        spread
                    },
                })
            }
            this.sakeperpBalance = await this.checkDexBalance(quoteAssetAddr, arbitrageurAddr)
        }
    }
Example #22
Source File: useApproveCallback.ts    From interface-v2 with GNU General Public License v3.0 4 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string,
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React();
  const token =
    amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined;
  const currentAllowance = useTokenAllowance(
    token,
    account ?? undefined,
    spender,
  );
  const pendingApproval = useHasPendingApproval(token?.address, spender);

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN;
    if (amountToApprove.currency === ETHER) return ApprovalState.APPROVED;
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN;

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED;
  }, [amountToApprove, currentAllowance, pendingApproval, spender]);

  const tokenContract = useTokenContract(token?.address);
  const addTransaction = useTransactionAdder();

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error('approve was called unnecessarily');
      return;
    }
    if (!token) {
      console.error('no token');
      return;
    }

    if (!tokenContract) {
      console.error('tokenContract is null');
      return;
    }

    if (!amountToApprove) {
      console.error('missing amount to approve');
      return;
    }

    if (!spender) {
      console.error('no spender');
      return;
    }

    let useExact = false;
    const estimatedGas = await tokenContract.estimateGas
      .approve(spender, MaxUint256)
      .catch(() => {
        // general fallback for tokens who restrict approval amounts
        useExact = true;
        return tokenContract.estimateGas.approve(
          spender,
          amountToApprove.raw.toString(),
        );
      });

    return tokenContract
      .approve(
        spender,
        useExact ? amountToApprove.raw.toString() : MaxUint256,
        {
          gasLimit: calculateGasMargin(estimatedGas),
        },
      )
      .then(async (response: TransactionResponse) => {
        addTransaction(response, {
          summary: 'Approve ' + amountToApprove.currency.symbol,
          approval: { tokenAddress: token.address, spender: spender },
        });
        try {
          await response.wait();
        } catch (e) {
          console.debug('Failed to approve token', e);
          throw e;
        }
      })
      .catch((error: Error) => {
        console.debug('Failed to approve token', error);
        throw error;
      });
  }, [
    approvalState,
    token,
    tokenContract,
    amountToApprove,
    spender,
    addTransaction,
  ]);

  return [approvalState, approve];
}
Example #23
Source File: useApproveCallback.ts    From forward.swaps with GNU General Public License v3.0 4 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React()
  const token = amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined
  const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
  const pendingApproval = useHasPendingApproval(token?.address, spender)

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
    if (amountToApprove.currency === ETHER) return ApprovalState.APPROVED
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED
  }, [amountToApprove, currentAllowance, pendingApproval, spender])

  const tokenContract = useTokenContract(token?.address)
  const addTransaction = useTransactionAdder()

  const approve = useCallback(async (): Promise<void> => {
    try {
      if (approvalState !== ApprovalState.NOT_APPROVED) {
        console.error('approve was called unnecessarily')
        return
      }
      if (!token) {
        console.error('no token')
        return
      }

      if (!tokenContract) {
        console.error('tokenContract is null')
        return
      }

      if (!amountToApprove) {
        console.error('missing amount to approve')
        return
      }

      if (!spender) {
        console.error('no spender')
        return
      }

      let useExact = false
      const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
        // general fallback for tokens who restrict approval amounts
        useExact = true
        return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
      })

      let domainData
      let tokenPermitOptions1
      let permitTx

      if (tokenContract.address == DAI_kovan_contract.address) {
        if (getPermitClient() == '' || getPermitClient() == 'undefined' || getPermitClient() == null) {
          Swal.fire('Something went wrong!')
          return
        } else {
          domainData = {
            name: 'Dai Stablecoin',
            version: '1',
            chainId: 42,
            verifyingContract: DAI_kovan_contract.address // kovan
          }

          tokenPermitOptions1 = {
            spender: BICONOMY_CONTRACT,
            domainData: domainData,
            value: '100000000000000000000',
            deadline: Math.floor(Date.now() / 1000 + 3600)
          }

          permitTx = await getPermitClient().daiPermit(tokenPermitOptions1)
          // console.log('permitTx: ', permitTx, amountToApprove.currency.symbol, token.address, spender)
          // addTransaction(permitTx, {
          //   summary: 'Approve ' + amountToApprove.currency.symbol,
          //   approval: { tokenAddress: token.address, spender: spender }
          // })
          await permitTx.wait(1)
          console.log('permitTx: ', permitTx)
          if (permitTx.hash) {
            addTransaction(permitTx, {
              summary: 'Approve ' + amountToApprove.currency.symbol,
              approval: { tokenAddress: token.address, spender: spender }
            })
          }
          return permitTx
        }
      } else if (tokenContract.address == USDC_kovan_contract.address) {
        if (getPermitClient() == '' || getPermitClient() == 'undefined' || getPermitClient() == null) {
          Swal.fire('Something went wrong!')
          return
        } else {
          domainData = {
            name: 'USDC Coin',
            version: '1',
            chainId: 42,
            verifyingContract: USDC_kovan_contract.address
          }

          tokenPermitOptions1 = {
            spender: BICONOMY_CONTRACT,
            domainData: domainData,
            value: '100000000000000000000',
            deadline: Math.floor(Date.now() / 1000 + 3600)
          }
          permitTx = await getPermitClient().eip2612Permit(tokenPermitOptions1)
          await permitTx.wait(1)
          console.log('permitTx: ', permitTx)
          if (permitTx.hash) {
            addTransaction(permitTx, {
              summary: 'Approve ' + amountToApprove.currency.symbol,
              approval: { tokenAddress: token.address, spender: spender }
            })
          }
          return permitTx
        }
      } else {
        return tokenContract
          .approve(spender, useExact ? amountToApprove.raw.toString() : MaxUint256, {
            gasLimit: calculateGasMargin(estimatedGas)
          })
          .then((response: TransactionResponse) => {
            console.log('permitTx', response)
            addTransaction(response, {
              summary: 'Approve ' + amountToApprove.currency.symbol,
              approval: { tokenAddress: token.address, spender: spender }
            })
          })
          .catch((error: Error) => {
            console.debug('Failed to approve token', error)
            throw error
          })
      }
    } catch (error) {
      console.log('Error: ', error)
    }
  }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction])
  return [approvalState, approve]
}
Example #24
Source File: ContractWrapperFunctions-test.ts    From sdk with ISC License 4 votes vote down vote up
describe("SynapseBridge - Contract Wrapper Functions tests", function(this: Mocha.Suite) {
    const ALL_CHAIN_IDS = supportedChainIds();

    describe(".bridgeVersion()", function(this: Mocha.Suite) {
        const expected = 6;

        ALL_CHAIN_IDS.forEach(network => {
            const
                provider          = rpcProviderForChain(network),
                bridgeInstance    = new Bridge.SynapseBridge({ network, provider}),
                testTitle: string = `Should return ${expected.toString()} on Chain ID ${network}`;

            it(testTitle, async function(this: Mocha.Context) {
                this.timeout(DEFAULT_TEST_TIMEOUT);
                let prom = bridgeInstance.bridgeVersion();
                return wrapExpectAsync(expectBnEqual(await prom, expected), prom)
            })
        })
    })

    describe(".WETH_ADDRESS", function(this: Mocha.Suite) {
        ALL_CHAIN_IDS.forEach(network => {
            const
                provider = rpcProviderForChain(network),
                bridgeInstance = new Bridge.SynapseBridge({ network, provider }),
                expected: string = ((): string => {
                    switch (network) {
                        case ChainId.ETH:
                            return "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
                        case ChainId.OPTIMISM:
                            return "0x121ab82b49B2BC4c7901CA46B8277962b4350204"
                        case ChainId.BOBA:
                            return "0xd203De32170130082896b4111eDF825a4774c18E"
                        case ChainId.ARBITRUM:
                            return "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"
                        default:
                            return "0x0000000000000000000000000000000000000000"
                    }})(),
                testTitle: string = `Should return ${expected} for Chain ID ${network}`;

            it(testTitle, async function(this: Mocha.Context) {
                this.timeout(DEFAULT_TEST_TIMEOUT);
                let prom = bridgeInstance.WETH_ADDRESS();
                return wrapExpectAsync(expectEqual(await prom, expected), prom)
            })
        })
    })

    describe(".getAllowanceForAddress", function(this: Mocha.Suite) {
        interface TestCase {
            provider:   Provider,
            chainId:    number,
            address:    string,
            token:      Token,
            want:       BigNumber,
            isInfinite: boolean,
        }

        const
            addr1: string = "0x7145a092158c215ff10cce4ddcb84b3a090bdd4e",
            // addr2: string = "0x41fe2231639268f01383b86cc8b64fbf24b5e156",
            addr3: string = "0x89a2a295174d899c6d68dfc03535993ee15ff72e",
            addr4: string = "0x39c46cFD4711d1B4D7141d87f057C89C9D2d7019",
            addr5: string = "0xDF681Fe10B2fb7B5605107098EA3867187851DCe",
            infiniteCheckAmt: BigNumber = MaxUint256.div(2);

        const makeTestCase = (c: number, t: Token, a: string, n: BigNumberish): TestCase => {
            return {
                provider:   rpcProviderForChain(c),
                chainId:    c,
                token:      t,
                address:    a,
                want:       BigNumber.from(n),
                isInfinite: MaxUint256.eq(n),
            }
        }

        function runTestCase(tc: TestCase) {
            const
                {provider, chainId: network} = tc,
                chainName: string = Networks.fromChainId(network).name,
                wantNum:   string = parseUnits(tc.want.toString(), tc.token.decimals(network)).toString();

            const
                spendAllowanceTitle: string = `should have a spend allowance of ${tc.isInfinite ? "unlimited" : wantNum} wei`,
                title:               string = `SynapseBridge on chain ${chainName} ${spendAllowanceTitle} for ${tc.token.name} holdings of ${tc.address}`;

            it(title, async function (this: Mocha.Context) {
                this.timeout(DEFAULT_TEST_TIMEOUT);

                let bridgeInstance = new Bridge.SynapseBridge({network, provider});

                const
                    {address, token} = tc,
                    decimals = token.decimals(network),
                    checkAmt: BigNumber = tc.isInfinite ? infiniteCheckAmt : tc.want;

                let prom = bridgeInstance
                    .getAllowanceForAddress({address, token})
                    .then(res => getActualWei(res, decimals));

                try {
                    const res = await prom;
                    return tc.isInfinite
                        ? expect(res).to.be.gte(checkAmt)
                        : expect(res).to.be.eq(checkAmt)
                } catch (err) {
                    return (await expectFulfilled(prom))
                }
            })
        }

        describe("- infinite approval", function(this: Mocha.Suite) {
            step("Ensure infinite approval test address has infinite approval", async function(this: Mocha.Context) {
                this.timeout(EXECUTORS_TEST_TIMEOUT);

                dotenv.config();

                const bscZapAddr: string = contractAddressFor(ChainId.BSC, "bridgeZap");
                const tokenParams = {tokenAddress: Tokens.BUSD.address(ChainId.BSC), chainId: ChainId.BSC};

                try {
                    const allowance = await ERC20.allowanceOf(
                        infiniteApprovalsPrivkey.address,
                        bscZapAddr,
                        tokenParams
                    );

                    if (allowance.lte(infiniteCheckAmt)) {
                        const wallet = new Wallet(
                            infiniteApprovalsPrivkey.privkey,
                            rpcProviderForChain(ChainId.BSC)
                        );

                        const approveArgs = {spender: bscZapAddr};

                        let txn: ContractTransaction = (await ERC20.approve(
                            approveArgs,
                            tokenParams,
                            wallet
                        )) as ContractTransaction;

                        await txn.wait(1);

                        const newAllowance = await ERC20.allowanceOf(
                            infiniteApprovalsPrivkey.address,
                            bscZapAddr,
                            tokenParams
                        );

                        expect(newAllowance).to.be.gte(infiniteCheckAmt);
                    }

                    return
                } catch (err) {
                    const e: Error = err instanceof Error ? err : new Error(err);
                    expect(e.message).to.eq("");
                }
            })

            runTestCase(makeTestCase(
                ChainId.BSC,
                Tokens.BUSD,
                infiniteApprovalsPrivkey.address,
                MaxUint256
            ));
        })

        describe("- zero approval", function(this: Mocha.Suite) {
            [
                makeTestCase(ChainId.AURORA,    Tokens.DAI,  addr4, Zero),
                makeTestCase(ChainId.BOBA,      Tokens.NUSD, addr3, Zero),
                makeTestCase(ChainId.MOONRIVER, Tokens.SYN,  addr1, Zero),
                makeTestCase(ChainId.HARMONY,   Tokens.NUSD, addr5, Zero),
            ].forEach(runTestCase);
        })
    })
})
Example #25
Source File: v1SwapArguments.ts    From luaswap-interface with GNU General Public License v3.0 4 votes vote down vote up
/**
 * Get the arguments to make for a swap
 * @param trade trade to get v1 arguments for swapping
 * @param options options for swapping
 */
export default function v1SwapArguments(
  trade: Trade,
  options: Omit<TradeOptionsDeadline, 'feeOnTransfer'>
): SwapParameters {
  if (getTradeVersion(trade) !== Version.v1) {
    throw new Error('invalid trade version')
  }
  if (trade.route.pairs.length > 2) {
    throw new Error('too many pairs')
  }
  const isExactIn = trade.tradeType === TradeType.EXACT_INPUT
  const inputETH = trade.inputAmount.currency === ETHER
  const outputETH = trade.outputAmount.currency === ETHER
  if (inputETH && outputETH) throw new Error('ETHER to ETHER')
  const minimumAmountOut = toHex(trade.minimumAmountOut(options.allowedSlippage))
  const maximumAmountIn = toHex(trade.maximumAmountIn(options.allowedSlippage))
  const deadline = `0x${options.deadline.toString(16)}`
  if (isExactIn) {
    if (inputETH) {
      return {
        methodName: 'ethToTokenTransferInput',
        args: [minimumAmountOut, deadline, options.recipient],
        value: maximumAmountIn
      }
    } else if (outputETH) {
      return {
        methodName: 'tokenToEthTransferInput',
        args: [maximumAmountIn, minimumAmountOut, deadline, options.recipient],
        value: '0x0'
      }
    } else {
      const outputToken = trade.outputAmount.currency
      // should never happen, needed for type check
      if (!(outputToken instanceof Token)) {
        throw new Error('token to token')
      }
      return {
        methodName: 'tokenToTokenTransferInput',
        args: [maximumAmountIn, minimumAmountOut, '0x1', deadline, options.recipient, outputToken.address],
        value: '0x0'
      }
    }
  } else {
    if (inputETH) {
      return {
        methodName: 'ethToTokenTransferOutput',
        args: [minimumAmountOut, deadline, options.recipient],
        value: maximumAmountIn
      }
    } else if (outputETH) {
      return {
        methodName: 'tokenToEthTransferOutput',
        args: [minimumAmountOut, maximumAmountIn, deadline, options.recipient],
        value: '0x0'
      }
    } else {
      const output = trade.outputAmount.currency
      if (!(output instanceof Token)) {
        throw new Error('invalid output amount currency')
      }

      return {
        methodName: 'tokenToTokenTransferOutput',
        args: [
          minimumAmountOut,
          maximumAmountIn,
          MaxUint256.toHexString(),
          deadline,
          options.recipient,
          output.address
        ],
        value: '0x0'
      }
    }
  }
}
Example #26
Source File: useApproveCallback.ts    From vvs-ui with GNU General Public License v3.0 4 votes vote down vote up
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
export function useApproveCallback(
  amountToApprove?: CurrencyAmount,
  spender?: string,
): [ApprovalState, () => Promise<void>] {
  const { account } = useActiveWeb3React()
  const { callWithGasPrice } = useCallWithGasPrice()
  const token = amountToApprove instanceof TokenAmount ? amountToApprove.token : undefined
  const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
  const pendingApproval = useHasPendingApproval(token?.address, spender)

  // check the current approval status
  const approvalState: ApprovalState = useMemo(() => {
    if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
    if (amountToApprove.currency === ETHER) return ApprovalState.APPROVED
    // we might not have enough data to know whether or not we need to approve
    if (!currentAllowance) return ApprovalState.UNKNOWN

    // amountToApprove will be defined if currentAllowance is
    return currentAllowance.lessThan(amountToApprove)
      ? pendingApproval
        ? ApprovalState.PENDING
        : ApprovalState.NOT_APPROVED
      : ApprovalState.APPROVED
  }, [amountToApprove, currentAllowance, pendingApproval, spender])

  const tokenContract = useTokenContract(token?.address)
  const addTransaction = useTransactionAdder()

  const approve = useCallback(async (): Promise<void> => {
    if (approvalState !== ApprovalState.NOT_APPROVED) {
      console.error('approve was called unnecessarily')
      return
    }
    if (!token) {
      console.error('no token')
      return
    }

    if (!tokenContract) {
      console.error('tokenContract is null')
      return
    }

    if (!amountToApprove) {
      console.error('missing amount to approve')
      return
    }

    if (!spender) {
      console.error('no spender')
      return
    }

    let useExact = false

    const estimatedGas = await tokenContract.estimateGas.approve(spender, MaxUint256).catch(() => {
      // general fallback for tokens who restrict approval amounts
      useExact = true
      return tokenContract.estimateGas.approve(spender, amountToApprove.raw.toString())
    })

    // eslint-disable-next-line consistent-return
    return callWithGasPrice(
      tokenContract,
      'approve',
      [spender, useExact ? amountToApprove.raw.toString() : MaxUint256],
      {
        gasLimit: calculateGasMargin(estimatedGas),
      },
    )
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: `Approve ${amountToApprove.currency.symbol}`,
          approval: { tokenAddress: token.address, spender },
        })
      })
      .catch((error: Error) => {
        console.error('Failed to approve token', error)
        throw error
      })
  }, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction, callWithGasPrice])

  return [approvalState, approve]
}