bancorprotocol / carbon-app

https://app.carbondefi.xyz
MIT License
25 stars 21 forks source link

The URL of duplicate strategy is very long and probably contains unneeded info #1015

Closed zavelevsky closed 8 months ago

zavelevsky commented 8 months ago

The URL when duplicating a strategy contains a base64 encoded search param called 'strategy' with the following type of data:

{
   "id":"340282366920938463463374607431768211457",
   "idDisplay":"1",
   "base":{
      "symbol":"ETH",
      "address":"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
      "logoURI":"https://d1wmp5nysbq9xl.cloudfront.net/ethereum/tokens/0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.svg",
      "decimals":18,
      "name":"Ethereum"
   },
   "quote":{
      "chainId":1,
      "address":"0x6B175474E89094C44Da98b954EedeAC495271d0F",
      "name":"Dai",
      "symbol":"DAI",
      "decimals":18,
      "logoURI":"https://d1wmp5nysbq9xl.cloudfront.net/ethereum/tokens/0x6b175474e89094c44da98b954eedeac495271d0f.svg"
   },
   "order0":{
      "balance":"1.054950903769487627",
      "startRate":"1799.999999999990486394388497496028313238358720127507694996893405914306640625",
      "endRate":"1799.999999999990486394388497496028313238358720127507694996893405914306640625",
      "marginalRate":"1799.999999999990486394388497496028313238358720127507694996893405914306640625"
   },
   "order1":{
      "balance":"0.000537835017279437",
      "startRate":"1945.000000000134993102932750245637127047919646653228833706819444502760467212806615894706226293932838",
      "endRate":"1945.000000000134993102932750245637127047919646653228833706819444502760467212806615894706226293932838",
      "marginalRate":"1945.000000000134993102932750245637127047919646653228833706819444502760467212806615894706226293932838"
   },
   "status":"active",
   "encoded":{
      "id":"340282366920938463463374607431768211457",
      "token0":"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
      "token1":"0x6B175474E89094C44Da98b954EedeAC495271d0F",
      "order0":{
         "y":"537835017279437",
         "z":"1080226227443660",
         "A":"0",
         "B":"6382340776412"
      },
      "order1":{
         "y":"1054950903769487627",
         "z":"1937048429517186009",
         "A":"0",
         "B":"1875443170982464"
      }
   },
   "roi":"-1.290593073734033",
   "fiatBudget":{
      "base":"8.95899217868239462187",
      "quote":"7.4901514167633621517",
      "total":"16.44914359544575677357"
   }
}

It should actually only have the minimal data needed to create a strategy:

{
   "base":"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
   "quote":"0x6B175474E89094C44Da98b954EedeAC495271d0F",
   "order0":{
      "balance":"1.054950903769487627",
      "startRate":"1799.999999999990486394388497496028313238358720127507694996893405914306640625",
      "endRate":"1799.999999999990486394388497496028313238358720127507694996893405914306640625",
      "marginalRate":"1799.999999999990486394388497496028313238358720127507694996893405914306640625"
   },
   "order1":{
      "balance":"0.000537835017279437",
      "startRate":"1945.000000000134993102932750245637127047919646653228833706819444502760467212806615894706226293932838",
      "endRate":"1945.000000000134993102932750245637127047919646653228833706819444502760467212806615894706226293932838",
      "marginalRate":"1945.000000000134993102932750245637127047919646653228833706819444502760467212806615894706226293932838"
   },
}

Even this one is pretty long - both like this and as base64. We can consider encoding the numbers in an efficient way. A number such as "1945.000000000134993102932750245637127047919646653228833706819444502760467212806615894706226293932838"

can be converted to a much shorter representation by using a base much larger than 10 - but it needs to be applied to fractional part (after the dot) and the integer part separately, while addressing the leading 0s in the fractional part.

Just an idea. This can convert the number above to "eZ:9:RX0M1rPTdO4ij4hlkGcvKUZTZQFRmiabMROAV2A6JVMlkIMm" in the format of <base64(integer)>::<base64(fractional)> or just <base64(integer)> if there's no fractional part.

Here's the code to achieve this:

const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

function intToBase64(num: BigInt): string {
    let result = '';
    while (num > 0n) {
        result = base64Chars[Number(num % 64n)] + result;
        num /= 64n;
    }
    return result || "0";  // Handle the case when num is 0
}

function processNumberString(numberString: string): string {
    // Split into integer and fractional parts
    let [integerPartStr, fractionalPartStr] = numberString.split('.');
    let integerPart = BigInt(integerPartStr);
    let fractionalPart = fractionalPartStr || '';

    // Count leading zeros in the fractional part
    let leadingZeros = fractionalPart.length - fractionalPart.replace(/^0+/, '').length;

    // Convert integer part to base64
    let integerBase64 = intToBase64(integerPart);

    // If fractional part exists, convert it to base64
    if (fractionalPart) {
        // Remove leading zeros and convert the rest as BigInt
        let nonZeroFractionalPart = BigInt(fractionalPart.replace(/^0+/, ''));
        let fractionalBase64 = nonZeroFractionalPart > 0n ? intToBase64(nonZeroFractionalPart) : '';

        return `${integerBase64}:${leadingZeros}:${fractionalBase64}`;
    } else {
        return integerBase64;
    }
}

// -- the reverse direction

function base64ToInt(base64Str: string): BigInt {
    let result = BigInt(0);
    for (let char of base64Str) {
        result = result * 64n + BigInt(base64Chars.indexOf(char));
    }
    return result;
}

function reverseProcess(encodedStr: string): string {
    // Split the encoded string
    let parts = encodedStr.split(':');
    let integerBase64 = parts[0];
    let leadingZerosCount = parts.length > 1 ? parseInt(parts[1]) : 0;
    let fractionalBase64 = parts.length > 2 ? parts[2] : '';

    // Decode the integer part
    let integerPart = base64ToInt(integerBase64).toString();

    // Decode the fractional part and add leading zeros
    let fractionalPart = fractionalBase64 ? base64ToInt(fractionalBase64).toString() : '';
    fractionalPart = fractionalPart.padStart(leadingZerosCount + fractionalPart.length, '0');

    // Combine the parts to form the original number
    return fractionalPart ? `${integerPart}.${fractionalPart}` : integerPart;
}

By giving up on json style, shortening param names and not doing encode to base64 we can get it down to 340 chars:

"base":"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", "quote":"0x6B175474E89094C44Da98b954EedeAC495271d0F", "b0":"1:1:1P3O", "sR0":"GJ:15:1P3O0O1ME11BBH2C7", "eR0":"GJ:15:1P3O0O1ME11BBH2C7", "mR0":"GJ:15:1P3O0O1ME11BBH2C7", "b1":"0:3:PPP", "sR1":"GJ:15:1P3O0O1ME11BBH2C7", "eR1":"GJ:15:1P3O0O1ME11BBH2C7", "mR1":"GJ:15:1P3O0O1ME11BBH2C7"
zavelevsky commented 8 months ago

An alternative solution by @ashachaf is to achieve shorter numbers simply by cutting the number after the first 6 non 0 digits.