---
title: "మీరు కాష్ చేయగలిగినదంతా"
description: "చౌకైన రోలప్ లావాదేవీల కోసం కాషింగ్ కాంట్రాక్ట్‌ను ఎలా సృష్టించాలో మరియు ఎలా ఉపయోగించాలో తెలుసుకోండి"
author: "ఓరి పోమెరాంట్జ్"
tags: ["లేయర్ 2", "కాషింగ్", "నిల్వ", "స్కేలింగ్"]
skill: intermediate
breadcrumb: "రోల్అప్‌ల కోసం కాషింగ్"
published: 2022-09-15
lang: te
---

రోల్అప్‌లను ఉపయోగిస్తున్నప్పుడు లావాదేవీలోని ఒక బైట్ ఖర్చు నిల్వ స్లాట్ ఖర్చు కంటే చాలా ఖరీదైనది. కాబట్టి, సాధ్యమైనంత ఎక్కువ సమాచారాన్ని ఆన్‌చైన్‌లో కాష్ చేయడం సమంజసం.

ఈ కథనంలో, బహుళ సార్లు ఉపయోగించబడే అవకాశం ఉన్న ఏదైనా పారామీటర్ విలువ కాష్ చేయబడి, (మొదటి సారి తర్వాత) చాలా తక్కువ సంఖ్యలో బైట్‌లతో ఉపయోగించడానికి అందుబాటులో ఉండే విధంగా కాషింగ్ కాంట్రాక్ట్‌ను ఎలా సృష్టించాలో మరియు ఉపయోగించాలో, అలాగే ఈ కాష్‌ను ఉపయోగించే ఆఫ్‌చైన్ కోడ్‌ను ఎలా వ్రాయాలో మీరు నేర్చుకుంటారు.

మీరు కథనాన్ని దాటవేసి, కేవలం సోర్స్ కోడ్‌ను చూడాలనుకుంటే, [అది ఇక్కడ ఉంది](https://github.com/qbzzt/20220915-all-you-can-cache). డెవలప్‌మెంట్ స్టాక్ [Foundry](https://getfoundry.sh/introduction/installation/).

## మొత్తం డిజైన్ {#overall-design}

సరళత కోసం అన్ని లావాదేవీ పారామీటర్‌లు `uint256`, 32 బైట్‌ల పొడవు ఉన్నాయని మనం అనుకుందాం. మనం లావాదేవీని స్వీకరించినప్పుడు, ప్రతి పారామీటర్‌ను ఈ విధంగా అన్వయిస్తాము:

1. మొదటి బైట్ `0xFF` అయితే, తదుపరి 32 బైట్‌లను పారామీటర్ విలువగా తీసుకుని, దానిని కాష్‌కు వ్రాయండి.

2. మొదటి బైట్ `0xFE` అయితే, తదుపరి 32 బైట్‌లను పారామీటర్ విలువగా తీసుకోండి కానీ దానిని కాష్‌కు వ్రాయవద్దు (_not_).

3. మరే ఇతర విలువకైనా, ఎగువ నాలుగు బిట్‌లను అదనపు బైట్‌ల సంఖ్యగా మరియు దిగువ నాలుగు బిట్‌లను కాష్ కీ యొక్క అత్యంత ముఖ్యమైన బిట్‌లుగా తీసుకోండి. ఇక్కడ కొన్ని ఉదాహరణలు ఉన్నాయి:

   | కాల్ డేటాలోని బైట్‌లు | కాష్ కీ |
   | :---------------- | --------: |
   | 0x0F              |      0x0F |
   | 0x10,0x10         |      0x10 |
   | 0x12,0xAC         |    0x02AC |
   | 0x2D,0xEA, 0xD6   |  0x0DEAD6 |

## కాష్ మానిప్యులేషన్ {#cache-manipulation}

కాష్ [`Cache.sol`](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol)లో అమలు చేయబడింది. దానిని లైన్ బై లైన్ పరిశీలిద్దాం.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;


contract Cache {

    bytes1 public constant INTO_CACHE = 0xFF;
    bytes1 public constant DONT_CACHE = 0xFE;
```

మనం మొత్తం సమాచారాన్ని అందించే మరియు దానిని కాష్‌లోకి వ్రాయాలనుకుంటున్నామా లేదా అనే ప్రత్యేక సందర్భాలను అర్థం చేసుకోవడానికి ఈ స్థిరాంకాలు ఉపయోగించబడతాయి. కాష్‌లోకి వ్రాయడానికి గతంలో ఉపయోగించని నిల్వ స్లాట్‌లలోకి రెండు [`SSTORE`](https://www.evm.codes/#55) ఆపరేషన్‌లు అవసరం, ఒక్కొక్క దానికి 22100 గ్యాస్ ఖర్చవుతుంది, కాబట్టి మనం దానిని ఐచ్ఛికం చేస్తాము.

```solidity

    mapping(uint => uint) public val2key;
```

విలువల మరియు వాటి కీల మధ్య [మ్యాపింగ్](https://www.geeksforgeeks.org/solidity/solidity-mappings/). మీరు లావాదేవీని పంపే ముందు విలువలను ఎన్‌కోడ్ చేయడానికి ఈ సమాచారం అవసరం.

```solidity
    // స్థానం n, కీ n+1 కోసం విలువను కలిగి ఉంటుంది, ఎందుకంటే మనం భద్రపరచాలి
    // సున్నాను "కాష్‌లో లేదు" అని.
    uint[] public key2val;
```

కీల నుండి విలువలకు మ్యాపింగ్ చేయడానికి మనం శ్రేణిని (array) ఉపయోగించవచ్చు ఎందుకంటే మనం కీలను కేటాయిస్తాము మరియు సరళత కోసం మనం దానిని వరుసగా చేస్తాము.

```solidity
    function cacheRead(uint _key) public view returns (uint) {
        require(_key <= key2val.length, "Reading uninitialize cache entry");
        return key2val[_key-1];
    }  // cacheRead
```

కాష్ నుండి విలువను చదవండి.

```solidity
    // విలువ ఇప్పటికే కాష్‌లో లేకుంటే దాన్ని కాష్‌లో రాయండి
    // పరీక్ష పనిచేయడానికి వీలుగా పబ్లిక్ మాత్రమే చేయబడింది
    function cacheWrite(uint _value) public returns (uint) {
        // విలువ ఇప్పటికే కాష్‌లో ఉంటే, ప్రస్తుత కీని తిరిగి ఇవ్వండి
        if (val2key[_value] != 0) {
            return val2key[_value];
        }
```

ఒకే విలువను కాష్‌లో ఒకటి కంటే ఎక్కువసార్లు ఉంచడంలో అర్థం లేదు. విలువ ఇప్పటికే ఉంటే, ఇప్పటికే ఉన్న కీని తిరిగి ఇవ్వండి.

```solidity
        // 0xFE అనేది ఒక ప్రత్యేక సందర్భం కాబట్టి, కాష్ కలిగి ఉండగల అతిపెద్ద కీ
        // 0x0D తర్వాత 15 0xFFలు. కాష్ పొడవు ఇప్పటికే అంత
        // పెద్దదిగా ఉంటే, విఫలం అవుతుంది.
        //                              1 2 3 4 5 6 7 8 9 A B C D E F
        require(key2val.length+1 < 0x0DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
            "cache overflow");
```

మనకు అంత పెద్ద కాష్ ఎప్పుడైనా వస్తుందని నేను అనుకోను (సుమారు 1.8\*10<sup>37</sup> ఎంట్రీలు, నిల్వ చేయడానికి సుమారు 10<sup>27</sup> TB అవసరం). అయినప్పటికీ, ["640kB ఎల్లప్పుడూ సరిపోతుంది"](https://quoteinvestigator.com/2011/09/08/640k-enough/) అని గుర్తుంచుకునేంత వయస్సు నాకు ఉంది. ఈ పరీక్ష చాలా చౌకైనది.

```solidity
        // తదుపరి కీని ఉపయోగించి విలువను రాయండి
        val2key[_value] = key2val.length+1;
```

రివర్స్ లుక్అప్‌ను జోడించండి (విలువ నుండి కీకి).

```solidity
        key2val.push(_value);
```

ఫార్వర్డ్ లుక్అప్‌ను జోడించండి (కీ నుండి విలువకు). మనం విలువలను వరుసగా కేటాయిస్తాము కాబట్టి మనం దానిని చివరి శ్రేణి విలువ తర్వాత జోడించవచ్చు.

```solidity
        return key2val.length;
    }  // cacheWrite
```

కొత్త విలువ నిల్వ చేయబడిన సెల్ అయిన `key2val` యొక్క కొత్త పొడవును తిరిగి ఇవ్వండి.

```solidity
    function _calldataVal(uint startByte, uint length)
        private pure returns (uint)
```

ఈ ఫంక్షన్ ఏకపక్ష పొడవు (32 బైట్‌ల వరకు, పద పరిమాణం) ఉన్న కాల్ డేటా నుండి విలువను చదువుతుంది.

```solidity
    {
        uint _retVal;

        require(length < 0x21,
            "_calldataVal length limit is 32 bytes");
        require(length + startByte <= msg.data.length,
            "_calldataVal trying to read beyond calldatasize");
```

ఈ ఫంక్షన్ అంతర్గతమైనది, కాబట్టి మిగిలిన కోడ్ సరిగ్గా వ్రాయబడితే ఈ పరీక్షలు అవసరం లేదు. అయినప్పటికీ, వాటికి ఎక్కువ ఖర్చు ఉండదు కాబట్టి మనం వాటిని ఉంచుకోవచ్చు.

```solidity
        assembly {
            _retVal := calldataload(startByte)
        }
```

ఈ కోడ్ [Yul](https://docs.soliditylang.org/en/v0.8.16/yul.html)లో ఉంది. ఇది కాల్ డేటా నుండి 32 బైట్ విలువను చదువుతుంది. EVMలో ప్రారంభించబడని స్థలం సున్నాగా పరిగణించబడుతుంది కాబట్టి కాల్ డేటా `startByte+32` కంటే ముందే ఆగిపోయినా ఇది పని చేస్తుంది.

```solidity
        _retVal = _retVal >> (256-length*8);
```

మనకు తప్పనిసరిగా 32 బైట్ విలువ అవసరం లేదు. ఇది అదనపు బైట్‌లను వదిలించుకుంటుంది.

```solidity
        return _retVal;
    } // _calldataVal


    // _fromByte వద్ద ప్రారంభించి, కాల్ డేటా నుండి ఒకే పరామితిని చదవండి
    function _readParam(uint _fromByte) internal
        returns (uint _nextByte, uint _parameterValue)
    {
```

కాల్ డేటా నుండి ఒకే పారామీటర్‌ను చదవండి. పారామీటర్‌లు 1 బైట్ పొడవు నుండి 33 బైట్‌ల వరకు ఉండవచ్చు కాబట్టి మనం చదివిన విలువను మాత్రమే కాకుండా, తదుపరి బైట్ యొక్క స్థానాన్ని కూడా తిరిగి ఇవ్వాలని గమనించండి.

```solidity
        // మిగిలిన వాటిని ఎలా అర్థం చేసుకోవాలో మొదటి బైట్ చెబుతుంది
        uint8 _firstByte;

        _firstByte = uint8(_calldataVal(_fromByte, 1));
```

ప్రమాదకరమైన [ఇంప్లిసిట్ టైప్ కన్వర్షన్‌లను](https://docs.soliditylang.org/en/v0.8.16/types.html#implicit-conversions) నిషేధించడం ద్వారా బగ్‌ల సంఖ్యను తగ్గించడానికి Solidity ప్రయత్నిస్తుంది. డౌన్‌గ్రేడ్, ఉదాహరణకు 256 బిట్‌ల నుండి 8 బిట్‌లకు, స్పష్టంగా ఉండాలి.

```solidity

        // విలువను చదవండి, కానీ దానిని కాష్‌లో రాయకండి
        if (_firstByte == uint8(DONT_CACHE))
            return(_fromByte+33, _calldataVal(_fromByte+1, 32));

        // విలువను చదవండి మరియు దానిని కాష్‌లో రాయండి
        if (_firstByte == uint8(INTO_CACHE)) {
            uint _param = _calldataVal(_fromByte+1, 32);
            cacheWrite(_param);
            return(_fromByte+33, _param);
        }

        // మనం ఇక్కడికి వచ్చామంటే, మనం కాష్ నుండి చదవాలని అర్థం

        // చదవడానికి అదనపు బైట్‌ల సంఖ్య
        uint8 _extraBytes = _firstByte / 16;
```

దిగువ [నిబుల్ (nibble)](https://en.wikipedia.org/wiki/Nibble)ని తీసుకుని, కాష్ నుండి విలువను చదవడానికి దానిని ఇతర బైట్‌లతో కలపండి.

```solidity
        uint _key = (uint256(_firstByte & 0x0F) << (8*_extraBytes)) +
            _calldataVal(_fromByte+1, _extraBytes);

        return (_fromByte+_extraBytes+1, cacheRead(_key));

    }  // _readParam


    // n పారామితులను చదవండి (ఫంక్షన్‌లు ఎన్ని పారామితులను ఆశిస్తున్నాయో వాటికి తెలుసు)
    function _readParams(uint _paramNum) internal returns (uint[] memory) {
```

కాల్ డేటా నుండే మన వద్ద ఉన్న పారామీటర్‌ల సంఖ్యను మనం పొందవచ్చు, కానీ మనల్ని పిలిచే ఫంక్షన్‌లకు అవి ఎన్ని పారామీటర్‌లను ఆశిస్తున్నాయో తెలుసు. వాటిని మనకు చెప్పనివ్వడం సులభం.

```solidity
        // మనం చదివిన పారామితులు
        uint[] memory params = new uint[](_paramNum);

        // పారామితులు బైట్ 4 వద్ద ప్రారంభమవుతాయి, అంతకు ముందు ఇది ఫంక్షన్ సంతకం
        uint _atByte = 4;

        for(uint i=0; i<_paramNum; i++) {
            (_atByte, params[i]) = _readParam(_atByte);
        }
```

మీకు అవసరమైన సంఖ్య వచ్చే వరకు పారామీటర్‌లను చదవండి. మనం కాల్ డేటా ముగింపును దాటితే, `_readParams` కాల్‌ను రివర్ట్ చేస్తుంది.

```solidity

        return(params);
    }   // readParams

    // _readParamsని పరీక్షించడానికి, నాలుగు పారామితులను చదవడం పరీక్షించండి
    function fourParam() public
        returns (uint256,uint256,uint256,uint256)
    {
        uint[] memory params;
        params = _readParams(4);
        return (params[0], params[1], params[2], params[3]);
    }    // fourParam
```

Foundry యొక్క ఒక పెద్ద ప్రయోజనం ఏమిటంటే ఇది పరీక్షలను Solidityలో వ్రాయడానికి అనుమతిస్తుంది ([దిగువన కాష్‌ను పరీక్షించడం చూడండి](#testing-the-cache)). ఇది యూనిట్ పరీక్షలను చాలా సులభతరం చేస్తుంది. ఇది నాలుగు పారామీటర్‌లను చదివి వాటిని తిరిగి ఇచ్చే ఫంక్షన్, తద్వారా అవి సరైనవో కాదో పరీక్ష ధృవీకరించగలదు.

```solidity
    // ఒక విలువను పొందండి, దాన్ని ఎన్‌కోడ్ చేసే బైట్‌లను తిరిగి ఇవ్వండి (వీలైతే కాష్‌ని ఉపయోగించి)
    function encodeVal(uint _val) public view returns(bytes memory) {
```

`encodeVal` అనేది కాష్‌ను ఉపయోగించే కాల్ డేటాను సృష్టించడంలో సహాయపడటానికి ఆఫ్‌చైన్ కోడ్ కాల్ చేసే ఫంక్షన్. ఇది ఒకే విలువను స్వీకరిస్తుంది మరియు దానిని ఎన్‌కోడ్ చేసే బైట్‌లను తిరిగి ఇస్తుంది. ఈ ఫంక్షన్ ఒక `view`, కాబట్టి దీనికి లావాదేవీ అవసరం లేదు మరియు బాహ్యంగా కాల్ చేసినప్పుడు ఎటువంటి గ్యాస్ ఖర్చు ఉండదు.

```solidity
        uint _key = val2key[_val];

        // విలువ ఇంకా కాష్‌లో లేదు, దాన్ని జోడించండి
        if (_key == 0)
            return bytes.concat(INTO_CACHE, bytes32(_val));
```

[EVM](/developers/docs/evm/)లో ప్రారంభించబడని నిల్వ అంతా సున్నాలుగా భావించబడుతుంది. కాబట్టి మనం లేని విలువ కోసం కీని వెతికితే, మనకు సున్నా వస్తుంది. ఆ సందర్భంలో దానిని ఎన్‌కోడ్ చేసే బైట్‌లు `INTO_CACHE` (కాబట్టి ఇది తదుపరిసారి కాష్ చేయబడుతుంది), ఆపై అసలు విలువ ఉంటుంది.

```solidity
        // కీ <0x10 అయితే, దాన్ని ఒకే బైట్‌గా తిరిగి ఇవ్వండి
        if (_key < 0x10)
            return bytes.concat(bytes1(uint8(_key)));
```

సింగిల్ బైట్‌లు అత్యంత సులభమైనవి. `bytes<n>` రకాన్ని ఏ పొడవుకైనా ఉండగల బైట్ శ్రేణిగా మార్చడానికి మనం కేవలం [`bytes.concat`](https://docs.soliditylang.org/en/v0.8.16/types.html#the-functions-bytes-concat-and-string-concat)ని ఉపయోగిస్తాము. పేరు అలా ఉన్నప్పటికీ, కేవలం ఒక ఆర్గ్యుమెంట్‌తో అందించినప్పుడు ఇది బాగా పనిచేస్తుంది.

```solidity
        // రెండు బైట్ల విలువ, 0x1vvvగా ఎన్‌కోడ్ చేయబడింది
        if (_key < 0x1000)
            return bytes.concat(bytes2(uint16(_key) | 0x1000));
```

మనకు 16<sup>3</sup> కంటే తక్కువ కీ ఉన్నప్పుడు, మనం దానిని రెండు బైట్‌లలో వ్యక్తపరచవచ్చు. మనం మొదట 256 బిట్ విలువ అయిన `_key`ని 16 బిట్ విలువగా మారుస్తాము మరియు మొదటి బైట్‌కు అదనపు బైట్‌ల సంఖ్యను జోడించడానికి లాజికల్ ORని ఉపయోగిస్తాము. ఆపై మనం దానిని `bytes2` విలువగా మారుస్తాము, దానిని `bytes`గా మార్చవచ్చు.

```solidity
        // కింది పంక్తులను లూప్‌గా చేయడానికి బహుశా ఒక తెలివైన మార్గం ఉండవచ్చు,
        // కానీ ఇది వ్యూ ఫంక్షన్ కాబట్టి నేను ప్రోగ్రామర్ సమయం మరియు
        // సరళత కోసం ఆప్టిమైజ్ చేస్తున్నాను.

        if (_key < 16*256**2)
            return bytes.concat(bytes3(uint24(_key) | (0x2 * 16 * 256**2)));
        if (_key < 16*256**3)
            return bytes.concat(bytes4(uint32(_key) | (0x3 * 16 * 256**3)));
             .
             .
             .
        if (_key < 16*256**14)
            return bytes.concat(bytes15(uint120(_key) | (0xE * 16 * 256**14)));
        if (_key < 16*256**15)
            return bytes.concat(bytes16(uint128(_key) | (0xF * 16 * 256**15)));
```

ఇతర విలువలు (3 బైట్‌లు, 4 బైట్‌లు మొదలైనవి) అదే విధంగా నిర్వహించబడతాయి, కేవలం విభిన్న ఫీల్డ్ పరిమాణాలతో.

```solidity
        // మనం ఇక్కడికి వస్తే, ఏదో తప్పు జరిగినట్లు.
        revert("Error in encodeVal, should not happen");
```

మనం ఇక్కడికి చేరుకున్నామంటే మనకు 16\*256<sup>15</sup> కంటే తక్కువ కాని కీ వచ్చిందని అర్థం. కానీ `cacheWrite` కీలను పరిమితం చేస్తుంది కాబట్టి మనం 14\*256<sup>16</sup> వరకు కూడా పొందలేము (దీని మొదటి బైట్ 0xFE ఉంటుంది, కాబట్టి ఇది `DONT_CACHE` లాగా కనిపిస్తుంది). కానీ భవిష్యత్తులో ప్రోగ్రామర్ బగ్‌ను ప్రవేశపెడితే ఒక పరీక్షను జోడించడానికి మనకు పెద్దగా ఖర్చు కాదు.

```solidity
    } // encodeVal

}  // Cache
```

### కాష్‌ను పరీక్షించడం {#testing-the-cache}

Foundry యొక్క ప్రయోజనాల్లో ఒకటి ఏమిటంటే [ఇది పరీక్షలను Solidityలో వ్రాయడానికి మిమ్మల్ని అనుమతిస్తుంది](https://getfoundry.sh/forge/tests/overview/), ఇది యూనిట్ పరీక్షలను వ్రాయడాన్ని సులభతరం చేస్తుంది. `Cache` క్లాస్ కోసం పరీక్షలు [ఇక్కడ](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/Cache.t.sol) ఉన్నాయి. పరీక్షలు సాధారణంగా పునరావృతమవుతాయి కాబట్టి, ఈ కథనం ఆసక్తికరమైన భాగాలను మాత్రమే వివరిస్తుంది.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Test.sol";


// కన్సోల్ కోసం `forge test -vv` రన్ చేయాలి.
import "forge-std/console.sol";
```

ఇది కేవలం పరీక్ష ప్యాకేజీ మరియు `console.log`ని ఉపయోగించడానికి అవసరమైన బాయిలర్‌ప్లేట్.

```solidity
import "src/Cache.sol";
```

మనం పరీక్షిస్తున్న కాంట్రాక్ట్ గురించి తెలుసుకోవాలి.

```solidity
contract CacheTest is Test {
    Cache cache;

    function setUp() public {
        cache = new Cache();
    }
```

ప్రతి పరీక్షకు ముందు `setUp` ఫంక్షన్ కాల్ చేయబడుతుంది. ఈ సందర్భంలో మనం కేవలం కొత్త కాష్‌ను సృష్టిస్తాము, తద్వారా మన పరీక్షలు ఒకదానికొకటి ప్రభావితం చేయవు.

```solidity
    function testCaching() public {
```

పరీక్షలు అనేవి `test`తో ప్రారంభమయ్యే పేర్లను కలిగి ఉన్న ఫంక్షన్‌లు. ఈ ఫంక్షన్ ప్రాథమిక కాష్ కార్యాచరణను తనిఖీ చేస్తుంది, విలువలను వ్రాయడం మరియు వాటిని మళ్లీ చదవడం చేస్తుంది.

```solidity
        for(uint i=1; i<5000; i++) {
            cache.cacheWrite(i*i);
        }

        for(uint i=1; i<5000; i++) {
            assertEq(cache.cacheRead(i), i*i);
```

[`assert...` ఫంక్షన్‌లను](https://getfoundry.sh/reference/forge-std/std-assertions/) ఉపయోగించి మీరు అసలు పరీక్షను ఈ విధంగా చేస్తారు. ఈ సందర్భంలో, మనం వ్రాసిన విలువ మనం చదివినదేనా అని తనిఖీ చేస్తాము. కాష్ కీలు సరళంగా కేటాయించబడతాయని మనకు తెలుసు కాబట్టి మనం `cache.cacheWrite` ఫలితాన్ని విస్మరించవచ్చు.

```solidity
        }
    }    // testCaching


    // ఒకే విలువను బహుళ సార్లు కాష్ చేయండి, కీ అలాగే ఉండేలా చూసుకోండి
    // అదే విధంగా
    function testRepeatCaching() public {
        for(uint i=1; i<100; i++) {
            uint _key1 = cache.cacheWrite(i);
            uint _key2 = cache.cacheWrite(i);
            assertEq(_key1, _key2);
        }
```

మొదట మనం ప్రతి విలువను కాష్‌కు రెండుసార్లు వ్రాస్తాము మరియు కీలు ఒకేలా ఉన్నాయని నిర్ధారించుకుంటాము (అంటే రెండవ వ్రాత నిజంగా జరగలేదు).

```solidity
        for(uint i=1; i<100; i+=3) {
            uint _key = cache.cacheWrite(i);
            assertEq(_key, i);
        }
    }    // testRepeatCaching
```

సిద్ధాంతపరంగా వరుస కాష్ వ్రాతలను ప్రభావితం చేయని బగ్ ఉండవచ్చు. కాబట్టి ఇక్కడ మనం వరుసగా లేని కొన్ని వ్రాతలను చేస్తాము మరియు విలువలు ఇప్పటికీ తిరిగి వ్రాయబడలేదని చూస్తాము.

```solidity
    // మెమరీ బఫర్ నుండి uintని చదవండి (మనం పంపిన పారామితులను తిరిగి పొందుతామని నిర్ధారించుకోవడానికి
    // మనం పంపినవి)
    function toUint256(bytes memory _bytes, uint256 _start) internal pure
        returns (uint256)
```

`bytes memory` బఫర్ నుండి 256 బిట్ పదాన్ని చదవండి. కాష్‌ను ఉపయోగించే ఫంక్షన్ కాల్‌ను రన్ చేసినప్పుడు మనం సరైన ఫలితాలను అందుకుంటామని ధృవీకరించడానికి ఈ యుటిలిటీ ఫంక్షన్ అనుమతిస్తుంది.

```solidity
    {
        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }
```

Yul `uint256`కి మించిన డేటా నిర్మాణాలకు మద్దతు ఇవ్వదు, కాబట్టి మీరు మెమరీ బఫర్ `_bytes` వంటి మరింత అధునాతన డేటా నిర్మాణాన్ని సూచించినప్పుడు, మీకు ఆ నిర్మాణం యొక్క చిరునామా వస్తుంది. Solidity `bytes memory` విలువలను పొడవును కలిగి ఉన్న 32 బైట్ పదంగా నిల్వ చేస్తుంది, ఆపై అసలు బైట్‌లు ఉంటాయి, కాబట్టి బైట్ సంఖ్య `_start`ని పొందడానికి మనం `_bytes+32+_start`ని లెక్కించాలి.

```solidity

        return tempUint;
    }     // toUint256

    // fourParams() కోసం ఫంక్షన్ సంతకం, సౌజన్యంతో
    // https://www.4byte.directory/signatures/?bytes4_signature=0x3edc1e6d
    bytes4 constant FOUR_PARAMS = 0x3edc1e6d;

    // మనం సరైన విలువలను తిరిగి పొందుతున్నామో లేదో చూడటానికి కొన్ని స్థిరమైన విలువలు
    uint256 constant VAL_A = 0xDEAD60A7;
    uint256 constant VAL_B =     0xBEEF;
    uint256 constant VAL_C =     0x600D;
    uint256 constant VAL_D = 0x600D60A7;
```

పరీక్ష కోసం మనకు అవసరమైన కొన్ని స్థిరాంకాలు.

```solidity
    function testReadParam() public {
```

మనం పారామీటర్‌లను సరిగ్గా చదవగలమో లేదో పరీక్షించడానికి, `readParams`ని ఉపయోగించే ఫంక్షన్ అయిన `fourParams()`ని కాల్ చేయండి.

```solidity
        address _cacheAddr = address(cache);
        bool _success;
        bytes memory _callInput;
        bytes memory _callOutput;
```

కాష్‌ని ఉపయోగించి ఫంక్షన్‌ను కాల్ చేయడానికి మనం సాధారణ ABI యంత్రాంగాన్ని ఉపయోగించలేము, కాబట్టి మనం తక్కువ స్థాయి [`<address>.call()`](https://docs.soliditylang.org/en/v0.8.16/types.html#members-of-addresses) యంత్రాంగాన్ని ఉపయోగించాలి. ఆ యంత్రాంగం `bytes memory`ని ఇన్‌పుట్‌గా తీసుకుంటుంది మరియు దానిని (అలాగే బూలియన్ విలువను) అవుట్‌పుట్‌గా తిరిగి ఇస్తుంది.

```solidity
        // మొదటి కాల్, కాష్ ఖాళీగా ఉంది
        _callInput = bytes.concat(
            FOUR_PARAMS,
```

ఒకే కాంట్రాక్ట్ కాష్ చేయబడిన ఫంక్షన్‌లకు (లావాదేవీల నుండి నేరుగా కాల్‌ల కోసం) మరియు కాష్ చేయబడని ఫంక్షన్‌లకు (ఇతర స్మార్ట్ కాంట్రాక్ట్‌ల నుండి కాల్‌ల కోసం) మద్దతు ఇవ్వడం ఉపయోగకరంగా ఉంటుంది. అలా చేయడానికి మనం ప్రతిదీ [ఒక `fallback` ఫంక్షన్‌లో](https://docs.soliditylang.org/en/v0.8.16/contracts.html#fallback-function) ఉంచడానికి బదులుగా, సరైన ఫంక్షన్‌ను కాల్ చేయడానికి Solidity యంత్రాంగంపై ఆధారపడటం కొనసాగించాలి. ఇలా చేయడం వల్ల కూర్పు సామర్థ్యం చాలా సులభం అవుతుంది. చాలా సందర్భాలలో ఫంక్షన్‌ను గుర్తించడానికి ఒకే బైట్ సరిపోతుంది, కాబట్టి మనం మూడు బైట్‌లను (16\*3=48 గ్యాస్) వృధా చేస్తున్నాము. అయినప్పటికీ, నేను దీనిని వ్రాస్తున్నప్పుడు ఆ 48 గ్యాస్ ధర 0.07 సెంట్లు, ఇది సరళమైన, తక్కువ బగ్‌లకు గురయ్యే కోడ్ యొక్క సహేతుకమైన ఖర్చు.

```solidity
            // మొదటి విలువ, దాన్ని కాష్‌కు జోడించండి
            cache.INTO_CACHE(),
            bytes32(VAL_A),
```

మొదటి విలువ: ఇది కాష్‌కు వ్రాయవలసిన పూర్తి విలువ అని చెప్పే ఫ్లాగ్, ఆపై విలువ యొక్క 32 బైట్‌లు ఉంటాయి. `VAL_B` కాష్‌కు వ్రాయబడదు మరియు `VAL_C` మూడవ పారామీటర్ మరియు నాల్గవది రెండూ కావడం మినహా మిగిలిన మూడు విలువలు ఒకేలా ఉంటాయి.

```solidity
             .
             .
             .
        );
        (_success, _callOutput) = _cacheAddr.call(_callInput);
```

ఇక్కడే మనం వాస్తవానికి `Cache` కాంట్రాక్ట్‌ను కాల్ చేస్తాము.

```solidity
        assertEq(_success, true);
```

కాల్ విజయవంతమవుతుందని మనం ఆశిస్తున్నాము.

```solidity
        assertEq(cache.cacheRead(1), VAL_A);
        assertEq(cache.cacheRead(2), VAL_C);
```

మనం ఖాళీ కాష్‌తో ప్రారంభించి, ఆపై `VAL_A`ని మరియు దాని తర్వాత `VAL_C`ని జోడిస్తాము. మొదటి దానికి కీ 1, మరియు రెండవ దానికి 2 ఉంటుందని మనం ఆశిస్తాము.

```
assertEq(toUint256(_callOutput,0), VAL_A);
        assertEq(toUint256(_callOutput,32), VAL_B);
        assertEq(toUint256(_callOutput,64), VAL_C);
        assertEq(toUint256(_callOutput,96), VAL_C);
```

అవుట్‌పుట్ నాలుగు పారామీటర్‌లు. ఇక్కడ మనం అది సరైనదో కాదో ధృవీకరిస్తాము.

```solidity
        // రెండవ కాల్, మనం కాష్‌ని ఉపయోగించవచ్చు
        _callInput = bytes.concat(
            FOUR_PARAMS,

            // కాష్‌లోని మొదటి విలువ
            bytes1(0x01),
```

16 కంటే తక్కువ ఉన్న కాష్ కీలు కేవలం ఒక బైట్ మాత్రమే.

```solidity
            // రెండవ విలువ, దాన్ని కాష్‌కు జోడించవద్దు
            cache.DONT_CACHE(),
            bytes32(VAL_B),

            // మూడవ మరియు నాల్గవ విలువలు, ఒకే విలువ
            bytes1(0x02),
            bytes1(0x02)
        );
        .
        .
        .
    }   // testReadParam
```

కాల్ తర్వాత పరీక్షలు మొదటి కాల్ తర్వాత ఉన్న వాటితో సమానంగా ఉంటాయి.

```solidity
    function testEncodeVal() public {
```

ఈ ఫంక్షన్ `testReadParam`ని పోలి ఉంటుంది, అయితే పారామీటర్‌లను స్పష్టంగా వ్రాయడానికి బదులుగా మనం `encodeVal()`ని ఉపయోగిస్తాము.

```solidity
        .
        .
        .
        _callInput = bytes.concat(
            FOUR_PARAMS,
            cache.encodeVal(VAL_A),
            cache.encodeVal(VAL_B),
            cache.encodeVal(VAL_C),
            cache.encodeVal(VAL_D)
        );
        .
        .
        .
        assertEq(_callInput.length, 4+1*4);
    }   // testEncodeVal
```

`testEncodeVal()`లో ఉన్న ఏకైక అదనపు పరీక్ష ఏమిటంటే `_callInput` పొడవు సరైనదో కాదో ధృవీకరించడం. మొదటి కాల్ కోసం ఇది 4+33\*4. ప్రతి విలువ ఇప్పటికే కాష్‌లో ఉన్న రెండవ దానికి, ఇది 4+1\*4.

```solidity
    // కీ ఒకే బైట్ కంటే ఎక్కువగా ఉన్నప్పుడు encodeValని పరీక్షించండి
    // గరిష్టంగా మూడు బైట్‌లు ఎందుకంటే కాష్‌ను నాలుగు బైట్‌లకు నింపడానికి
    // చాలా సమయం పడుతుంది.
    function testEncodeValBig() public {
        // కాష్‌లో అనేక విలువలను ఉంచండి.
        // విషయాలను సరళంగా ఉంచడానికి, విలువ n కోసం కీ nని ఉపయోగించండి.
        for(uint i=1; i<0x1FFF; i++) {
            cache.cacheWrite(i);
        }
```

పై `testEncodeVal` ఫంక్షన్ కాష్‌లోకి నాలుగు విలువలను మాత్రమే వ్రాస్తుంది, కాబట్టి [బహుళ-బైట్ విలువలతో వ్యవహరించే ఫంక్షన్ భాగం](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol#L144-L171) తనిఖీ చేయబడదు. కానీ ఆ కోడ్ సంక్లిష్టమైనది మరియు లోపాలకు గురయ్యే అవకాశం ఉంది.

ఈ ఫంక్షన్ యొక్క మొదటి భాగం 1 నుండి 0x1FFF వరకు ఉన్న అన్ని విలువలను క్రమంలో కాష్‌కు వ్రాసే లూప్, కాబట్టి మనం ఆ విలువలను ఎన్‌కోడ్ చేయగలుగుతాము మరియు అవి ఎక్కడికి వెళ్తున్నాయో తెలుసుకోగలుగుతాము.

```solidity
        .
        .
        .

        _callInput = bytes.concat(
            FOUR_PARAMS,
            cache.encodeVal(0x000F),   // ఒక బైట్        0x0F
            cache.encodeVal(0x0010),   // రెండు బైట్‌లు     0x1010
            cache.encodeVal(0x0100),   // రెండు బైట్‌లు     0x1100
            cache.encodeVal(0x1000)    // మూడు బైట్‌లు 0x201000
        );
```

ఒక బైట్, రెండు బైట్ మరియు మూడు బైట్ విలువలను పరీక్షించండి. తగినన్ని స్టాక్ ఎంట్రీలను వ్రాయడానికి చాలా సమయం పడుతుంది కాబట్టి (కనీసం 0x10000000, సుమారు పావు బిలియన్) మనం అంతకు మించి పరీక్షించము.

```solidity
        .
        .
        .
        .
    }    // testEncodeValBig


    // అతి చిన్న బఫర్‌తో మనం రివర్ట్ పొందుతామో లేదో పరీక్షించండి
    function testShortCalldata() public {
```

తగినన్ని పారామీటర్‌లు లేని అసాధారణ సందర్భంలో ఏమి జరుగుతుందో పరీక్షించండి.

```solidity
        .
        .
        .
        (_success, _callOutput) = _cacheAddr.call(_callInput);
        assertEq(_success, false);
    }   // testShortCalldata
```

ఇది రివర్ట్ అవుతుంది కాబట్టి, మనకు రావలసిన ఫలితం `false`.

```
// లేని కాష్ కీలతో కాల్ చేయండి
    function testNoCacheKey() public {
        .
        .
        .
        _callInput = bytes.concat(
            FOUR_PARAMS,

            // మొదటి విలువ, దానిని కాష్‌కు జోడించండి
            cache.INTO_CACHE(),
            bytes32(VAL_A),

            // రెండవ విలువ
            bytes1(0x0F),
            bytes2(0x1234),
            bytes11(0xA10102030405060708090A)
        );
```

ఈ ఫంక్షన్ నాలుగు ఖచ్చితమైన చట్టబద్ధమైన పారామీటర్‌లను పొందుతుంది, అయితే కాష్ ఖాళీగా ఉంది కాబట్టి అక్కడ చదవడానికి విలువలు లేవు.

```solidity
        .
        .
        .
    // అతి పొడవైన బఫర్‌తో అంతా బాగా పనిచేస్తుందో లేదో పరీక్షించండి
    function testLongCalldata() public {
        address _cacheAddr = address(cache);
        bool _success;
        bytes memory _callInput;
        bytes memory _callOutput;

        // మొదటి కాల్, కాష్ ఖాళీగా ఉంది
        _callInput = bytes.concat(
            FOUR_PARAMS,

            // మొదటి విలువ, దాన్ని కాష్‌కు జోడించండి
            cache.INTO_CACHE(), bytes32(VAL_A),

            // రెండవ విలువ, దాన్ని కాష్‌కు జోడించండి
            cache.INTO_CACHE(), bytes32(VAL_B),

            // మూడవ విలువ, దాన్ని కాష్‌కు జోడించండి
            cache.INTO_CACHE(), bytes32(VAL_C),

            // నాల్గవ విలువ, దాన్ని కాష్‌కు జోడించండి
            cache.INTO_CACHE(), bytes32(VAL_D),

            // మరియు "గుడ్ లక్" కోసం మరొక విలువ
            bytes4(0x31112233)
        );
```

ఈ ఫంక్షన్ ఐదు విలువలను పంపుతుంది. ఐదవ విలువ విస్మరించబడిందని మనకు తెలుసు ఎందుకంటే ఇది చెల్లుబాటు అయ్యే కాష్ ఎంట్రీ కాదు, ఒకవేళ అది చేర్చబడకపోతే రివర్ట్‌కు కారణమయ్యేది.

```solidity
        (_success, _callOutput) = _cacheAddr.call(_callInput);
        assertEq(_success, true);
        .
        .
        .
    }   // testLongCalldata

}        // CacheTest

```

## ఒక నమూనా అప్లికేషన్ {#a-sample-app}

Solidityలో పరీక్షలు వ్రాయడం చాలా మంచిది, కానీ అంతిమంగా ఒక వికేంద్రీకృత అప్లికేషన్ (dapp) ఉపయోగకరంగా ఉండాలంటే చైన్ వెలుపల నుండి వచ్చే అభ్యర్థనలను ప్రాసెస్ చేయగలగాలి. ఈ కథనం "రైట్ వన్స్, రీడ్ మెనీ" (Write Once, Read Many) అని అర్థం వచ్చే `WORM`తో డాప్‌లో కాషింగ్‌ను ఎలా ఉపయోగించాలో ప్రదర్శిస్తుంది. కీ ఇంకా వ్రాయబడకపోతే, మీరు దానికి విలువను వ్రాయవచ్చు. కీ ఇప్పటికే వ్రాయబడి ఉంటే, మీకు రివర్ట్ వస్తుంది.

### కాంట్రాక్ట్ {#the-contract}

[ఇది కాంట్రాక్ట్](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/WORM.sol). ఇది ఎక్కువగా మనం ఇప్పటికే `Cache` మరియు `CacheTest`తో చేసిన వాటినే పునరావృతం చేస్తుంది, కాబట్టి మనం ఆసక్తికరంగా ఉన్న భాగాలను మాత్రమే కవర్ చేస్తాము.

```solidity
import "./Cache.sol";

contract WORM is Cache {
```

`Cache`ని ఉపయోగించడానికి సులభమైన మార్గం దానిని మన స్వంత కాంట్రాక్ట్‌లో వారసత్వంగా పొందడం.

```solidity
    function writeEntryCached() external {
        uint[] memory params = _readParams(2);
        writeEntry(params[0], params[1]);
    }    // writeEntryCached
```

ఈ ఫంక్షన్ పైన ఉన్న `CacheTest`లోని `fourParam`ని పోలి ఉంటుంది. మనం ABI స్పెసిఫికేషన్‌లను అనుసరించము కాబట్టి, ఫంక్షన్‌లోకి ఎటువంటి పారామీటర్‌లను ప్రకటించకపోవడం ఉత్తమం.

```solidity
    // మమ్మల్ని కాల్ చేయడం సులభతరం చేయండి
    // writeEntryCached() కోసం ఫంక్షన్ సంతకం, సౌజన్యంతో
    // https://www.4byte.directory/signatures/?bytes4_signature=0xe4e4f2d3
    bytes4 constant public WRITE_ENTRY_CACHED = 0xe4e4f2d3;
```

`writeEntryCached`ని కాల్ చేసే బాహ్య కోడ్ `worm.writeEntryCached`ని ఉపయోగించడానికి బదులుగా కాల్ డేటాను మాన్యువల్‌గా నిర్మించవలసి ఉంటుంది, ఎందుకంటే మనం ABI స్పెసిఫికేషన్‌లను అనుసరించము. ఈ స్థిరమైన విలువను కలిగి ఉండటం వలన దానిని వ్రాయడం సులభం అవుతుంది.

మనం `WRITE_ENTRY_CACHED`ని స్టేట్ వేరియబుల్‌గా నిర్వచించినప్పటికీ, దానిని బాహ్యంగా చదవడానికి దాని కోసం గెట్టర్ ఫంక్షన్ `worm.WRITE_ENTRY_CACHED()`ని ఉపయోగించడం అవసరమని గమనించండి.

```solidity
    function readEntry(uint key) public view
        returns (uint _value, address _writtenBy, uint _writtenAtBlock)
```

రీడ్ ఫంక్షన్ ఒక `view`, కాబట్టి దీనికి లావాదేవీ అవసరం లేదు మరియు గ్యాస్ ఖర్చు ఉండదు. ఫలితంగా, పారామీటర్ కోసం కాష్‌ను ఉపయోగించడం వల్ల ఎటువంటి ప్రయోజనం లేదు. వ్యూ ఫంక్షన్‌లతో సరళమైన ప్రామాణిక యంత్రాంగాన్ని ఉపయోగించడం ఉత్తమం.

### టెస్టింగ్ కోడ్ {#the-testing-code}

[ఇది కాంట్రాక్ట్ కోసం టెస్టింగ్ కోడ్](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/WORM.t.sol). మళ్ళీ, ఆసక్తికరంగా ఉన్న వాటిని మాత్రమే చూద్దాం.

```solidity
    function testWReadWrite() public {
        worm.writeEntry(0xDEAD, 0x60A7);

        vm.expectRevert(bytes("entry already written"));
        worm.writeEntry(0xDEAD, 0xBEEF);
```

తదుపరి కాల్ విఫలం కావాలని మరియు వైఫల్యానికి నివేదించబడిన కారణాన్ని Foundry పరీక్షలో మనం [ఈ విధంగా (`vm.expectRevert`)](https://book.getfoundry.sh/cheatcodes/expect-revert#expectrevert) పేర్కొంటాము. కాల్ డేటాను నిర్మించడం మరియు తక్కువ స్థాయి ఇంటర్‌ఫేస్ (`<contract>.call()` మొదలైనవి) ఉపయోగించి కాంట్రాక్ట్‌ను కాల్ చేయడానికి బదులుగా మనం `<contract>.<function name>()` సింటాక్స్‌ను ఉపయోగించినప్పుడు ఇది వర్తిస్తుంది.

```solidity
    function testReadWriteCached() public {
        uint cacheGoat = worm.cacheWrite(0x60A7);
```

ఇక్కడ మనం `cacheWrite` కాష్ కీని తిరిగి ఇస్తుంది అనే వాస్తవాన్ని ఉపయోగిస్తాము. ఇది మనం ప్రొడక్షన్‌లో ఉపయోగిస్తామని ఆశించేది కాదు, ఎందుకంటే `cacheWrite` స్థితిని మారుస్తుంది, కాబట్టి లావాదేవీ సమయంలో మాత్రమే కాల్ చేయబడుతుంది. లావాదేవీలకు రిటర్న్ విలువలు ఉండవు, వాటికి ఫలితాలు ఉంటే ఆ ఫలితాలు ఈవెంట్‌లుగా విడుదల చేయబడతాయి. కాబట్టి `cacheWrite` రిటర్న్ విలువ ఆన్‌చైన్ కోడ్ నుండి మాత్రమే యాక్సెస్ చేయబడుతుంది మరియు ఆన్‌చైన్ కోడ్‌కు పారామీటర్ కాషింగ్ అవసరం లేదు.

```solidity
        (_success,) = address(worm).call(_callInput);
```

`<contract address>.call()`కి రెండు రిటర్న్ విలువలు ఉన్నప్పటికీ, మనం మొదటి దాని గురించి మాత్రమే పట్టించుకుంటామని Solidityకి ఈ విధంగా చెబుతాము.

```solidity
        (_success,) = address(worm).call(_callInput);
        assertEq(_success, false);
```

మనం తక్కువ స్థాయి `<address>.call()` ఫంక్షన్‌ను ఉపయోగిస్తాము కాబట్టి, మనం `vm.expectRevert()`ని ఉపయోగించలేము మరియు కాల్ నుండి మనకు లభించే బూలియన్ సక్సెస్ విలువను చూడాలి.

```solidity
    event EntryWritten(uint indexed key, uint indexed value);

        .
        .
        .

        _callInput = bytes.concat(
            worm.WRITE_ENTRY_CACHED(), worm.encodeVal(a), worm.encodeVal(b));
        vm.expectEmit(true, true, false, false);
        emit EntryWritten(a, b);
        (_success,) = address(worm).call(_callInput);
```

Foundryలో కోడ్ [ఈవెంట్‌ను సరిగ్గా విడుదల చేస్తుందని](https://getfoundry.sh/reference/cheatcodes/expect-emit/) మనం ధృవీకరించే విధానం ఇది.

### క్లయింట్ {#the-client}

Solidity పరీక్షలతో మీకు లభించని ఒక విషయం ఏమిటంటే, మీరు మీ స్వంత అప్లికేషన్‌లో కట్ చేసి పేస్ట్ చేయగల JavaScript కోడ్. ఈ ట్యుటోరియల్ యొక్క అసలు వెర్షన్ WORMని Optimism Goerliకి డిప్లాయ్ చేసింది, అది అప్పటి నుండి విరమించబడింది. ఈ రోజు క్లయింట్‌ను రన్ చేయడానికి, [OP Sepolia](https://docs.optimism.io/op-stack/introduction/op-stack) వంటి మద్దతు ఉన్న OP స్టాక్ నెట్‌వర్క్‌కు WORMని మళ్లీ డిప్లాయ్ చేయండి, ఆపై ఫలితంగా వచ్చిన కాంట్రాక్ట్ చిరునామాను JavaScript క్లయింట్‌లో ఉపయోగించండి.

[మీరు క్లయింట్ కోసం JavaScript కోడ్‌ను ఇక్కడ చూడవచ్చు](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/javascript/index.js). నమూనా రిపోజిటరీ Optimism Goerli కోసం వ్రాయబడింది, కాబట్టి దానిని రన్ చేయడానికి ముందు, మీ లక్ష్య నెట్‌వర్క్ కోసం `javascript/.env.example` మరియు `javascript/index.js`లో RPC ఎండ్‌పాయింట్ మరియు ఎక్స్‌ప్లోరర్ URLలను అప్‌డేట్ చేయండి. దీన్ని ఉపయోగించడానికి:

1. git రిపోజిటరీని క్లోన్ చేయండి:

   ```sh
   git clone https://github.com/qbzzt/20220915-all-you-can-cache.git
   ```

2. అవసరమైన ప్యాకేజీలను ఇన్‌స్టాల్ చేయండి:

   ```sh
   cd javascript
   yarn
   ```

3. కాన్ఫిగరేషన్ ఫైల్‌ను కాపీ చేయండి:

   ```sh
   cp .env.example .env
   ```

4. మీ కాన్ఫిగరేషన్ కోసం `.env`ని సవరించండి:

   | పారామీటర్           | విలువ                                                                                                                                                               |
   | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
   | MNEMONIC            | లావాదేవీకి చెల్లించడానికి తగినంత ETH ఉన్న ఖాతా కోసం నిమోనిక్ (mnemonic). [Optimism యొక్క ఫాసెట్ డాక్స్](https://docs.optimism.io/app-developers/tools/faucets) ప్రస్తుత టెస్ట్‌నెట్ ఫాసెట్‌లను జాబితా చేస్తాయి. |
   | OPTIMISM_GOERLI_URL | మీరు WORMని మళ్లీ డిప్లాయ్ చేసే నెట్‌వర్క్ కోసం RPC URL. OP Sepolia కోసం, `https://sepolia.optimism.io` వంటి OP Sepolia RPC ఎండ్‌పాయింట్‌ను లేదా మీ ప్రొవైడర్ నుండి మరొక ఎండ్‌పాయింట్‌ను ఉపయోగించండి.        |

5. `index.js`ని రన్ చేయండి.

   ```sh
   node index.js
   ```

   ఈ నమూనా అప్లికేషన్ మొదట WORMకి ఒక ఎంట్రీని వ్రాస్తుంది, కాల్ డేటాను మరియు బ్లాక్ ఎక్స్‌ప్లోరర్‌లో లావాదేవీకి లింక్‌ను ప్రదర్శిస్తుంది. ఆపై అది ఆ ఎంట్రీని తిరిగి చదువుతుంది మరియు అది ఉపయోగించే కీని మరియు ఎంట్రీలోని విలువలను (విలువ, బ్లాక్ నంబర్ మరియు రచయిత) ప్రదర్శిస్తుంది.

క్లయింట్‌లో ఎక్కువ భాగం సాధారణ Dapp JavaScript. కాబట్టి మళ్ళీ మనం ఆసక్తికరమైన భాగాలను మాత్రమే పరిశీలిస్తాము.

```javascript
.
.
.
const main = async () => {
    const func = await worm.WRITE_ENTRY_CACHED()

    // ప్రతిసారీ కొత్త కీ అవసరం
    const key = await worm.encodeVal(Number(new Date()))
```

ఒక నిర్దిష్ట స్లాట్‌లోకి ఒకసారి మాత్రమే వ్రాయవచ్చు, కాబట్టి మనం స్లాట్‌లను మళ్లీ ఉపయోగించకుండా చూసుకోవడానికి టైమ్‌స్టాంప్‌ను ఉపయోగిస్తాము.

```javascript
const val = await worm.encodeVal("0x600D")

// ఒక ఎంట్రీని వ్రాయండి
const calldata = func + key.slice(2) + val.slice(2)
```

Ethers కాల్ డేటా హెక్స్ స్ట్రింగ్‌గా ఉండాలని ఆశిస్తుంది, `0x` తర్వాత సరి సంఖ్యలో హెక్సాడెసిమల్ అంకెలు ఉంటాయి. `key` మరియు `val` రెండూ `0x`తో ప్రారంభమవుతాయి కాబట్టి, మనం ఆ హెడర్‌లను తీసివేయాలి.

```javascript
const tx = await worm.populateTransaction.writeEntryCached()
tx.data = calldata

sentTx = await wallet.sendTransaction(tx)
```

Solidity టెస్టింగ్ కోడ్ మాదిరిగానే, మనం కాష్ చేయబడిన ఫంక్షన్‌ను సాధారణంగా కాల్ చేయలేము. బదులుగా, మనం తక్కువ స్థాయి యంత్రాంగాన్ని ఉపయోగించాలి.

```javascript
    .
    .
    .
    // ఇప్పుడే వ్రాసిన ఎంట్రీని చదవండి
    const realKey = '0x' + key.slice(4)  // FF ఫ్లాగ్‌ను తీసివేయండి
    const entryRead = await worm.readEntry(realKey)
    .
    .
    .
```

ఎంట్రీలను చదవడానికి మనం సాధారణ యంత్రాంగాన్ని ఉపయోగించవచ్చు. `view` ఫంక్షన్‌లతో పారామీటర్ కాషింగ్‌ను ఉపయోగించాల్సిన అవసరం లేదు.
## ముగింపు {#conclusion}

ఈ కథనంలోని కోడ్ ఒక ప్రూఫ్ ఆఫ్ కాన్సెప్ట్, ఈ ఆలోచనను సులభంగా అర్థం చేసుకునేలా చేయడమే దీని ఉద్దేశ్యం. ప్రొడక్షన్-రెడీ సిస్టమ్ కోసం మీరు కొన్ని అదనపు కార్యాచరణలను అమలు చేయాలనుకోవచ్చు:

- `uint256` కాని విలువలను నిర్వహించండి. ఉదాహరణకు, స్ట్రింగ్‌లు.
- గ్లోబల్ కాష్‌కు బదులుగా, బహుశా వినియోగదారులు మరియు కాష్‌ల మధ్య మ్యాపింగ్‌ను కలిగి ఉండవచ్చు. వేర్వేరు వినియోగదారులు వేర్వేరు విలువలను ఉపయోగిస్తారు.
- చిరునామాల కోసం ఉపయోగించే విలువలు ఇతర ప్రయోజనాల కోసం ఉపయోగించే వాటికి భిన్నంగా ఉంటాయి. కేవలం చిరునామాల కోసం ప్రత్యేక కాష్‌ను కలిగి ఉండటం సమంజసం కావచ్చు.
- ప్రస్తుతం, కాష్ కీలు "ముందు వచ్చిన వారికి, చిన్న కీ" (first come, smallest key) అల్గారిథమ్‌పై ఉన్నాయి. మొదటి పదహారు విలువలను ఒకే బైట్‌గా పంపవచ్చు. తదుపరి 4080 విలువలను రెండు బైట్‌లుగా పంపవచ్చు. తదుపరి సుమారు మిలియన్ విలువలు మూడు బైట్‌లు మొదలైనవి. ప్రొడక్షన్ సిస్టమ్ కాష్ ఎంట్రీలపై వినియోగ కౌంటర్‌లను ఉంచాలి మరియు వాటిని పునర్వ్యవస్థీకరించాలి, తద్వారా పదహారు _అత్యంత సాధారణ_ విలువలు ఒక బైట్, తదుపరి 4080 అత్యంత సాధారణ విలువలు రెండు బైట్‌లు మొదలైనవి ఉంటాయి.

  అయితే, అది ప్రమాదకరమైన ఆపరేషన్ కావచ్చు. కింది సంఘటనల క్రమాన్ని ఊహించుకోండి:

  1. నోమ్ నైవ్ (Noam Naive) తాను టోకెన్‌లను పంపాలనుకుంటున్న చిరునామాను ఎన్‌కోడ్ చేయడానికి `encodeVal`ని కాల్ చేస్తాడు. ఆ చిరునామా అప్లికేషన్‌లో మొదట ఉపయోగించిన వాటిలో ఒకటి, కాబట్టి ఎన్‌కోడ్ చేయబడిన విలువ 0x06. ఇది ఒక `view` ఫంక్షన్, లావాదేవీ కాదు, కాబట్టి ఇది నోమ్ మరియు అతను ఉపయోగించే నోడ్ మధ్య ఉంటుంది మరియు దీని గురించి మరెవరికీ తెలియదు

  2. ఓవెన్ ఓనర్ (Owen Owner) కాష్ రీఆర్డరింగ్ ఆపరేషన్‌ను రన్ చేస్తాడు. వాస్తవానికి చాలా తక్కువ మంది ఆ చిరునామాను ఉపయోగిస్తారు, కాబట్టి ఇది ఇప్పుడు 0x201122గా ఎన్‌కోడ్ చేయబడింది. వేరొక విలువ, 10<sup>18</sup>కి 0x06 కేటాయించబడింది.

  3. నోమ్ నైవ్ తన టోకెన్‌లను 0x06కి పంపుతాడు. అవి `0x0000000000000000000000000de0b6b3a7640000` చిరునామాకు వెళ్తాయి మరియు ఆ చిరునామాకు సంబంధించిన ప్రైవేట్ కీ ఎవరికీ తెలియదు కాబట్టి, అవి అక్కడే ఇరుక్కుపోతాయి. నోమ్ _సంతోషంగా లేడు_.

  ఈ సమస్యను మరియు కాష్ రీఆర్డర్ సమయంలో మెంపూల్‌లో ఉన్న లావాదేవీల సంబంధిత సమస్యను పరిష్కరించడానికి మార్గాలు ఉన్నాయి, కానీ మీరు దాని గురించి తెలుసుకోవాలి.

నేను ఇక్కడ Optimismతో కాషింగ్‌ను ప్రదర్శించాను, ఎందుకంటే నేను Optimism ఉద్యోగిని మరియు నాకు బాగా తెలిసిన రోలప్ ఇదే. కానీ అంతర్గత ప్రాసెసింగ్ కోసం కనీస ఖర్చును వసూలు చేసే ఏ రోలప్‌తోనైనా ఇది పని చేయాలి, తద్వారా పోల్చి చూస్తే లావాదేవీ డేటాను L1కి వ్రాయడం ప్రధాన ఖర్చు అవుతుంది.

[నా మరిన్ని పనుల కోసం ఇక్కడ చూడండి](https://cryptodocguy.pro/).
