Rate Limits

How to implement rate limits?

Interactive Simulation

Implementation

const RATE_LIMIT = inputRateLimit;
const RATE_LIMIT_K2 = inputRateLimitK2;

const state = {
  valuesRaw: {
    carbon: 1100,         // tCO2eq
    kvcmAlloc: 200_000,   // kVCM
    k2Alloc: 200_000,     // K2
    kvcmTotal: 1_000_000, // kVCM
    k2Total: 1_000_000,   // K2
  },
  snapshots: {
    carbon: { value: 1100, time: 0, rate: RATE_LIMIT },
    kvcmAlloc: { value: 200_000, time: 0, rate: RATE_LIMIT },
    k2Alloc: { value: 200_000, time: 0, rate: RATE_LIMIT_K2 },
    kvcmTotal: { value: 1_000_000, time: 0, rate: RATE_LIMIT },
    k2Total: { value: 1_000_000, time: 0, rate: RATE_LIMIT_K2 },
  },
  time: 0,                // hours
};
const carbonDelta = +100; // tCO2eq
const time = 24;          // hours

function executeSwap(state, carbonDelta, time) {
  const { valuesRaw, snapshots } = state;
  const valuesEffective = computeEffectiveValues(valuesRaw, snapshots, time);
  const kvcmDelta = computePriceSwap(valuesEffective, carbonDelta);
  const stateNew = {
    valuesRaw: {
      ...valuesRaw,
      carbon: valuesRaw.carbon + carbonDelta,
      kvcmTotal: valuesRaw.kvcmTotal + kvcmDelta,
    },
    snapshots,
    time,
  };
  return stateNew;
}

function executeRetire(state, carbonDelta, time) {
  const { valuesRaw, snapshots } = state;
  const valuesEffective = computeEffectiveValues(valuesRaw, snapshots, time);
  const valuesRetirement = { ...valuesEffective, carbon: valuesRaw.carbon };
  const kvcmDelta = computePriceRetire(valuesRetirement, carbonDelta);
  const stateNew = {
    valuesRaw: {
      ...valuesRaw,
      carbon: valuesRaw.carbon + carbonDelta,
      kvcmTotal: valuesRaw.kvcmTotal + kvcmDelta,
    },
    snapshots: {
      ...snapshots,
      carbon: maybeUpdatedSnapshot(
        snapshots.carbon,
        valuesEffective.carbon,
        valuesRaw.carbon,
        time,
      ),
      kvcmTotal: maybeUpdatedSnapshot(
        snapshots.kvcmTotal,
        valuesEffective.kvcmTotal,
        valuesRaw.kvcmTotal,
        time,
      ),
    },
    time,
  };
  return stateNew;
}

function executeChange(state, name, delta, time) {
  if (name === "carbon") {
    throw new Error("Use `executeSwap` or `executeRetire` to change `carbon`");
  }
  const { valuesRaw, snapshots } = state;
  const valueEffective = computeEffectiveValue(
    name,
    valuesRaw,
    snapshots,
    time,
  );
  const stateNew = {
    valuesRaw: { ...valuesRaw, [name]: valuesRaw[name] + delta },
    snapshots: {
      ...snapshots,
      [name]: maybeUpdatedSnapshot(
        snapshots[name],
        valueEffective,
        valuesRaw[name],
        time,
      ),
    },
    time,
  };
  return stateNew;
}

function computeEffectiveValues(valuesRaw, snapshots, time) {
  const valuesEffective = {
    carbon: computeEffectiveValue("carbon", valuesRaw, snapshots, time),
    kvcmAlloc: computeEffectiveValue("kvcmAlloc", valuesRaw, snapshots, time),
    k2Alloc: computeEffectiveValue("k2Alloc", valuesRaw, snapshots, time),
    kvcmTotal: computeEffectiveValue("kvcmTotal", valuesRaw, snapshots, time),
    k2Total: computeEffectiveValue("k2Total", valuesRaw, snapshots, time),
  };
  return valuesEffective;
}

function computeEffectiveValue(name, valuesRaw, snapshots, time) {
  let valueEffective;
  if (name === "carbon" || name === "kvcmTotal" || name === "k2Total") {
    valueEffective = rateLimitedInv(valuesRaw[name], snapshots[name], time);
  } else {
    valueEffective = rateLimited(valuesRaw[name], snapshots[name], time);
  }
  return valueEffective;
}

function rateLimited(valueRaw, snapshot, time) {
  const timeDelta = time - snapshot.time;
  const valueLimit = snapshot.value * (1 + snapshot.rate * timeDelta);
  const valueEffective = Math.min(valueRaw, valueLimit);
  return valueEffective;
}

function rateLimitedInv(valueRaw, snapshot, time) {
  const timeDelta = time - snapshot.time;
  const valueLimit = snapshot.value / (1 + snapshot.rate * timeDelta);
  const valueEffective = Math.max(valueRaw, valueLimit);
  return valueEffective;
}

function maybeUpdatedSnapshot(snapshot, valueEffective, valueRaw, time) {
  if (valueEffective === valueRaw) {
    return { value: valueRaw, time: time, rate: snapshot.rate };
  }
  return snapshot;
}