forgxyz/anchor_dashboard

[Issue] Multiple anchor liquidation message in single transaciton.

Closed this issue · 2 comments

Why?

I am trying to build stg table for anchor liquidation, and I find the event_attributes is very complicated and multiple liquidation is usually involved. e.g. https://terrasco.pe/mainnet/tx/14B04061E46C4AE922CBE25A00AED77415E65F0C346C215097D57FE84AFC9303

What?

I've seen int_anchor_liquidations trying to select the first liquidation message from each tx. I have been thinking if any better way to select all pair of liquidations?

How?

One possible solution is UDF, which may be not efficient enough but more handy.

const parseAnchorLiquidate = (attrs) => {
  // Prepare map of attributes for all existing type
  var cache = {
    action: [],
    amount: [],
    borrower: [],
    liquidator: [],
    from: [],
    to: [],
    holder_address: [],
    stable_denom: [],
    repay_amount: [],
    bid_fee: [],
    liquidator_fee: [],
    collateral_token: [],
    collateral_amount: [],
    contract_address: [],
    provided_liquidity: [],
    addr_count: [],
  };

  // Classify all attributes by postfix, with single item type handled
  for (const [key, value] of Object.entries(attrs)) {
    // Pre-defined value for single item without {\d+_} prefix
    let keyIndex = 0,
      keyPostfix = key;

    if (/^\d/.test(key)) {
      let splitted = key.match(/^(\d+)_(.+)$/);
      keyIndex = parseInt(splitted[1]);
      keyPostfix = splitted[2];
    }

    cache[keyPostfix].push({ index: keyIndex, value: value });
  }

  // Each attributes are re-ordered by the integer prefix,
  // but not string comparison.
  for (const key of Object.keys(cache)) {
    cache[key] = cache[key]
      .sort((a, b) => a.index - b.index)
      .map((item) => item.value);
  }

  var result = [];

  // Based on the above map, we re-group all the attributes by action
  // assuming that all action's optional field exists.
  // Otherwise it will be so difficult to parse the data.
  for (const action of cache.action) {
    let amount,
      borrower,
      liquidator,
      from,
      to,
      holder_address,
      stable_denom,
      repay_amount,
      bid_fee,
      liquidator_fee,
      collateral_token,
      collateral_amount,
      contract_address;

    switch (action) {
      case "liquidate_collateral":
        liquidator = cache.liquidator.shift();
        amount = cache.amount.shift();
        borrower = cache.borrower.shift();
        contract_address = cache.contract_address.shift();
        result.push({
          action,
          liquidator,
          borrower,
          amount,
          contract_address,
        });
        break;
      case "execute_bid":
        stable_denom = cache.stable_denom.shift();
        repay_amount = cache.repay_amount.shift();
        bid_fee = cache.bid_fee.shift();
        liquidator_fee = cache.liquidator_fee.shift();
        collateral_token = cache.collateral_token.shift();
        collateral_amount = cache.collateral_amount.shift();
        contract_address = cache.contract_address.shift();
        result.push({
          action,
          stable_denom,
          repay_amount,
          bid_fee,
          liquidator_fee,
          collateral_token,
          collateral_amount,
          contract_address,
        });
        break;
      case "repay_stable":
        borrower = cache.borrower.shift();
        repay_amount = cache.repay_amount.shift();
        contract_address = cache.contract_address.shift();
        result.push({
          action,
          borrower,
          repay_amount,
          contract_address,
        });
        break;
      // we can even ignore all the result here, updating the array's index
      // is sufficient for the algo.
      case "send":
        to = cache.to.shift();
        from = cache.from.shift();
        amount = cache.amount.shift();
        contract_address = cache.contract_address.shift();
        result.push({ action, to, from, amount, contract_address });
        break;
      case "decrease_balance":
      case "increase_balance":
        holder_address = cache.holder_address.shift();
        amount = cache.amount.shift();
        contract_address = cache.contract_address.shift();
        result.push({ action, holder_address, amount, contract_address });
        break;
    }
  }

  // I am only interested in execute_bid and liquidate_collateral,
  // while repay_stable case should be also useful.
  cache = result.filter(
    (item) =>
      item.action === "execute_bid" || item.action === "liquidate_collateral"
  );

  result = [];

  // Again, wild assumption here that execute_bid and liquidate_collateral
  // actions are always in pairs.
  for (let i = 0; i < cache.length; i += 2) {
    chunk = cache.slice(i, i + 2);

    result.push({
      liquidator: chunk[0].liquidator,
      borrower: chunk[0].borrower,
      repay_amount: chunk[1].repay_amount,
      bid_fee: chunk[1].bid_fee,
      liquidator_fee: chunk[1].liquidator_fee,
      collateral_token: chunk[1].collateral_token,
      collateral_amount: chunk[1].collateral_amount,
    });
  }

  return result;
};

I updated the JS function above as tested to be working quite well. Still doing some testing / confirmation work but you can see if how the idea goes.

@kavimaluskam rip

if you are still interested in dbt and data curation, we are starting terra 2 models soon at MetricsDAO