Storage mortgage mechanism of Conflux

Posted by ptolomea on Fri, 14 Jan 2022 03:45:36 +0100

Background introduction

Recently, when developing smart contracts and deploying them to the Conflux network, some small partners in the community encountered the problem of pledging part of cfx when sending interaction to smart contracts and returning part of cfx after interaction problem.

As the roaster replied, in the blockchain, a certain amount of storage deposit needs to be charged according to the caller's occupation of smart contract space. This fee is usually higher than the actual need. When the space is released, a certain amount of GAS fee will be returned to the operator.

Conflux in its Protocol specification This mechanism is described in detail in technical secondary schools (see page 28).

Theoretical introduction

The collaborative for storage (CFS) mechanism is introduced into Conflux. As a pricing method for using storage, the CFS mechanism will be more fair and reasonable than the one-time storage cost in Ethereum. In principle, this mechanism needs to lock in a fund as collateral for occupying storage space. Before the corresponding storage space is released or covered by others, the collateral will be locked, and the corresponding interest generated by the locked collateral will be directly allocated to the miners for the maintenance of the storage space. Therefore, the storage cost of Conflux also depends on the length of time occupied by space.

In the Conflux network, the space occupied by each storage entry is 64B (B is Bytes), which is also the size of the key / value pair in the world state. It should be noted that in the blockchain, the key is generally 256bits long and the value is 256bits long (32B long respectively, 64B long combined). The deposit required for storage is proportional to the minimum multiple of 64B that can cover all stored items. For each storage entry, the account last written to the entry is called the owner of the storage entry. If a storage item is written in the execution of contract C and the guarantor provides guarantee, C is regarded as the writer of the item and becomes the owner accordingly (see Section 7.1 for details). In the world state, the owner of a storage item must lock a fixed number of CFX as the storage deposit occupying the storage space throughout the life cycle of the item. Specifically, the owner of each 64B storage entry will be locked by 1/16CFX. If 1KB space is occupied, 1CFX will be paid as the deposit, and the corresponding formula is as follows:

$$\left(\frac{1024}{64}\right)×\left(\frac{1}{16}\right)=1(CFX)$$

In account α When you become the owner of a storage item (whether created or modified), α 1/16 CFX should be locked for this entry immediately. If α If there is enough balance, the required deposit will be locked automatically. Otherwise, if α If there is not enough balance, the operation will fail, α The entry cannot be created or modified.

When a storage entry is deleted from the world state, the corresponding 1/16 CFX deposit will be unlocked and returned to the balance of the owner of the entry. If the ownership of a storage item changes, the 1/16 CFX deposit of the old owner is unlocked, while the new owner must lock 1/16 CFX as the deposit at the same time.

To facilitate processing, the function CFS is introduced into Conflux, which takes an account address α And a world state σ As input and return to the world state σ Next, account α Total locked deposit stored. If the world state σ To be clear from the context, we use CFS for brevity( α) Replace CFS( α;σ), The formula is as follows:

$$CFS( α) ≡CFS( α;σ) ≡ account a in world status σ Total number of storage entries owned under ×\ left(\frac{1}{16}\right)(CFX)$$

In particular, for a α= Transaction sent by S(T) (or α= T α If t calls address T α Sponsorship contract of the Department), order σ For the world state before and after t execution, σ' Is the world state after the transaction is executed. There is a CFS for the storage limit field Tl( α;σ) ≤CFS( α;σ)+ Assertion of Tl/1018.

#< Table > < tr > < TD bgcolor = yellow > key points: to find out how many CFX are pledged by calling the contract, you must find out the number of variables in the contract and how many entries are modified when operating through the function call contract, and the number of changes is recorded in the blockchain</ td></tr></table>

Solid memory management mechanism

According to the requirements for its memory management in the solid document describe And Conflux Deposit mechanism , we can find that the contract storage needs to be maintained with key and value. Generally, the length of key is 256bits, and the length of value is also 256bits. The storage space of a smart contract is organized according to the following table, where {0,1} 256 represents a 256 bit string (there are only two values of 0 or 1 in the bit string). Each key/value pair can be understood as an entry:

entryKey / address ({0,1} 256)Value ({0,1} 256)
10...000000
20...000011
30...000022
.........
2256f...fffff0

Since 256 bits = 32 bytes, the corresponding length of two 256 bits is:
$$32+32=64(Bytes)$$

Sorting of common variables and their corresponding entries in Solidity

variablelengthDefinition mode
Common variableOne common variable corresponds to one entryuint public count=0;
mappingEach key in mapping corresponds to one entrymapping(address => uint) public balances;
arrayEach element of the array corresponds to one entry, and the number leader arr.length is an additional entryuint[5] fixedArr = [1,2,3,4,5]; string productname;
structAccumulation of entries corresponding to each field in structstruct Person {uint age;uint stuID;string name;}

Description of storage deposit mechanism of Conflux

In Conflux's network, the cost of storing the deposit is 1 CFX per 1024 bytes. Since each entry occupies 64 bytes, the deposit cost of each entry is 1/16 CFX During the execution of each transaction, the newly generated deposit fee will be charged uniformly at the end of the execution of the transaction. If a storage entry is rewritten by someone else, the rewriter will pay the storage deposit, and the original deposit payer will get the returned deposit. It is worth mentioning that the deposit return is "quietly" added to the balance, and there is no transfer transaction for inquiry.

In each transaction of Conflux, you need to fill in a storage upper limit (in bytes). This cap stipulates that the deposit payer shall not increase the deposit by more than 1/1024 CFX before and after the transaction is executed If this value is too low, the post execution deposit will exceed the upper limit and the execution will fail. If you fill in too much, the sender's balance will not be enough to pay the deposit, and the transaction will also fail.

Deployment contract

Please refer to link , try to deploy and invoke the smart contract.

Example explanation

1. An example containing a common uint variable

The smart contract code is as follows:

pragma solidity ^0.5.0;

contract Counter {
    uint public count=0;
    event SelfEvent(address indexed sender, uint current);

    constructor() public {
    }

    function inc(uint num) public returns (uint){
        return count += num;
    }

    function self() public {
        emit SelfEvent(msg.sender, count);
    }
}

Because only uint public count=0 in the smart contract; A variable, corresponding to only one entry, is analyzed according to the previous formula:
$$CFS( α) ≡CFS( α;σ) ≡ account a in world status σ Total number of storage entries owned under ×\ left(\frac{1}{16}\right)(CFX)$$

It is precisely because the uint public count=0 variable exactly corresponds to a 64B entry. Combined with the example given before, the real pledge amount is 0.0625 CFX
$$\left(\frac{1}{16}\right)=0.0625(CFX)$$

The following is to verify the actual call of the contract:
The case of calling contract code is as follows (file name: call_calc.js):

const { Conflux, util } = require('js-conflux-sdk');
// This address is the receipt printed above contractCreated 
const public_address = '0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca';
const contractAddress = '0x845dd6f64bb3d2771a8f30dc85bb14f5ac26b75e';
const PRIVATE_KEY1 = '0x2772b19636f1d183a9a2a0d27da2a1d0efb97637b425********************';
const PRIVATE_KEY2 = '0x2adba218d5eacb5bc9bbb4c6fdecef7d1719c8184812********************';
const compiled = require(`./build/Counter.json`)
async function main() {
  const cfx = new Conflux({
    url: 'http://main.confluxrpc.org',
  });
  const contract = cfx.Contract({
    address : contractAddress,
    abi: compiled.abi,
  });
  
  const before_call_balance = await cfx.getBalance(public_address);
  console.log("before account1 call the current drip:"+before_call_balance.toString());
  console.log("before account1 call the current cfx:"+util.unit.fromDripToCFX(before_call_balance));
  
  let inc = await contract.inc(10);
  console.log("Output:"  + inc.toString());
  
  const account1 = cfx.Account(PRIVATE_KEY1);//Create account using private key
  
  // Record and spend CFX
  await contract.inc(10).sendTransaction({ from: account1 }).confirmed();
  
  const after_call_balance = await cfx.getBalance(public_address);
  console.log("after account1 call inc(10) the current drip:"+after_call_balance.toString());
  console.log("after account1 call inc(10) the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
    
  //Create account2 and try to call the contract to release cfx of account1
  const account2 = cfx.Account(PRIVATE_KEY2);//Create account using private key
  
  const before_account2_call_balance = await cfx.getBalance(public_address);
  console.log("before account2 call inc(5) the current drip:"+after_call_balance.toString());
  console.log("before account2 call inc(5) the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  await contract.inc(5).sendTransaction({ from: account2 }).confirmed();
  
  const after_account2_call_balance = await cfx.getBalance(public_address);
  console.log("after account2 call inc(10) the current drip:"+after_account2_call_balance.toString());
  console.log("after account2 call inc(10) the current cfx:"+util.unit.fromDripToCFX(after_account2_call_balance));
  
  
}
main().catch(e => console.error(e));

The calling method is:

node call_calc.js

For the convenience of description, the accounts involved in calling the contract are represented by account1 and account2, and the called contract is represented by contract. The account information and the corresponding account address are summarized as follows:

Account nameAccount address
account10x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca
account20x1941E3137aDDf02514cBFeC292710463d41e8196
countract0x845dd6f64bb3d2771a8f30dc85bb14f5ac26b75e

For convenience, the account name in the above table is used to refer to each account

Before calling, use confluxscan Check the CFX balance corresponding to account1 and find that the balance is 1994.680912261955354268 CFX.

(1) Use account1 for the first contract call:

Because the contract called by account1 takes up space, CFX needs to be handed in as a deposit

After the program starts: first display the account balance of account1: 1994.680912261955383167 CFX.

The program will use account1 to call contract Inc (10) initiates interaction with the contract. After the call is completed, it is found that the account balance of account1 will change to 1994.618412261955356217 CFX.

That is to say, after this interaction between account1 and contract, the account deducts:
$$1994.680912261955383167-1994.618412261955356217=0.06250000000002695(CFX)‬$$

This indicates that due to the call to contract Inc (10) interacts with the contract. account1 handed in CFX of 0.06250000000002695.

Use account2 to call the contract to help account1 free up space

The program will continue to run and use account2 by calling contract Inc (5) initiate interaction with the contract

Before calling, the account balance of account1 is 1994.618412261955356217 CFX, which is consistent with the account balance of account1 at the end of step (1).

After account2 invokes the contract, the CFX balance of account1 becomes 1994.680912261955356217 CFX

In other words, after the contract is called by account2, the CFX balance of account T1 changes to:
$$1994.618412261955356217-1994.680912261955356217=-0.0625(CFX)‬$$

This means that since the 64Bytes contract space occupied by account1 is released, 0.0625 CFX will be returned to account1's account. According to the payment amount calculated in step (1): 0.06250000000002695 CFX, we can infer that account1 calls contract The actual cost of Inc (10) is:
$$0.06250000000002695-0.0625=0.00000000000002695(CFX)$$

Before calling the contract, the CFX balance of account1 displayed by the program is 1994.618412261955437067 CFX.

After calling the contract, the CFX balance of the corresponding account is 1994.618412261955410117 CFX.

That is to say, after this interaction with the contract, 0.00000000000002695 CFX were deducted from the account, and the calculation formula is as follows:
$$1994.618412261955437067-1994.618412261955410117=0.00000000000002695(CFX)$$

This indirectly proves that the contract space occupied by data storage in account1 is one 64Bytes:
$$0.0625 × 16 = 1 (PCs.) ‬$$

At this time, we go to confluxscan to check the balance corresponding to account T1: 1994.680912261955327318 CFX

According to the calculation formula:
$$1994.680912261955354268-1994.680912261955327318=0.00000000000002695 (CFX)‬$$
This also proves that account1 calls contract Inc (10) actually consumed 0.00000000000002695 CFX in its account when interacting with the contract

2. An example of a fixed length array with a length of 5 (and 5 element values are modified when calling)

The contract code is as follows:

pragma solidity ^0.5.0;

contract Test {
    uint[5] arr = [1,2,3,4,5];
    event SelfEvent(address indexed sender, uint[5] current,uint length);
    
    function init() public{
        arr[0] = 100;
        arr[1] = 200;   
    }
    
    function getArrayContent() public returns(uint[5] memory){
        return arr;
    }
  
    function getArrayLength() public returns(uint){
        return arr.length;
    }
    
    function increment (uint data) public{
        for(uint i=0;i<arr.length;i++){
            arr[i]+=data;
        }
    }
    
    function getGrade() public returns (uint){
        uint grade = 0 ;
        for(uint i=0;i<arr.length;i++){
            grade += arr[i];
        }
        return grade;
    }
    function self() public {
        emit SelfEvent(msg.sender, arr,arr.length);
    }
}

Because only uint [] arr = [1,2,3,4,5] in the smart contract; For this array with a length of 5, the data in the array corresponds to 5 entries, and the array length of 5 also corresponds to 1 entry, a total of 6 entries. Since calling the increment() function in the contract will modify each element in the array, but the array length will not be changed, analyze according to the previous formula:
$$CFS( α) ≡CFS( α;σ) ≡ account a in world status σ Total number of storage entries owned under ×\ left(\frac{1}{16}\right)(CFX)$$

Because uint [] arr = [1,2,3,4,5]; The elements in the array and the length of the array exactly correspond to six entries with a length of 64B. However, since the length of the array has not changed and is written into the blockchain, combined with the example analysis given earlier, the amount of pledge should be 0.3125 CFX
$$\left(\frac{1}{16}\right)×5=0.3125(CFX)$$

The code for calling the contract is as follows:

const { Conflux, util } = require('js-conflux-sdk');
// This address is the receipt printed above contractCreated 
const public_address = '0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca';
const contractAddress = '0x822ebe7eb36cdf159d6d544f6321e1a5c6619dc2';
const PRIVATE_KEY1 = '0x2772b19636f1d183a9a2a0d27da2a1d0efb97637b425*';
const PRIVATE_KEY2 = '0x2adba218d5eacb5bc9bbb4c6fdecef7d1719c8184812*';
const compiled = require(`./build/Test.json`)
async function main() {
  const cfx = new Conflux({
    url: 'http://main.confluxrpc.org',
  });
  const contract = cfx.Contract({
    address : contractAddress,
    abi: compiled.abi,
  });
  
  let inc = await contract.getGrade();
  console.log("output:"  + inc.toString());
  
  const before_call_balance = await cfx.getBalance(public_address);
  console.log("before account1 call the current drip:"+before_call_balance.toString());
  console.log("before account1 call the current cfx:"+util.unit.fromDripToCFX(before_call_balance));
  
  const account1 = cfx.Account(PRIVATE_KEY1);//Create account using private key
  
  // Record and spend CFX
  await contract.increment(1).sendTransaction({ from: account1 }).confirmed();
  
  const after_call_balance = await cfx.getBalance(public_address);
  console.log("after account1 call increment() the current drip:"+after_call_balance.toString());
  console.log("after account1 call increment() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
    
  //Create account2 and try to call the contract to release cfx of account1
  const account2 = cfx.Account(PRIVATE_KEY2);//Create account using private key
  
  const before_account2_call_balance = await cfx.getBalance(public_address);
  console.log("before account2 call increment() the current drip:"+after_call_balance.toString());
  console.log("before account2 call increment() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  await contract.increment(2).sendTransaction({ from: account2 }).confirmed();
  
  const after_account2_call_balance = await cfx.getBalance(public_address);
  console.log("after account2 call increment() the current drip:"+after_account2_call_balance.toString());
  console.log("after account2 call increment() the current cfx:"+util.unit.fromDripToCFX(after_account2_call_balance));
}
main().catch(e => console.error(e));

For the convenience of description, the accounts involved in calling the contract are represented by account1 and account2, and the called contract is represented by contract. The account information and the corresponding account address are summarized as follows:

Account nameAccount address
account10x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca
account20x1941E3137aDDf02514cBFeC292710463d41e8196
countract0x822ebe7eb36cdf159d6d544f6321e1a5c6619dc2

For convenience, the account name in the above table is used to refer to each account

Before calling, use confluxscan Check the CFX balance corresponding to account1 and find that the balance is 1986.673099761952452902 CFX.

(1) Use account1 for the first contract call:

Since the increment() function called by account1 modifies the records stored in the contract, occupies the storage space, and the modified data is recorded by the blockchain, CFX needs to be submitted as a deposit

After the program starts: first display the account balance of account1: 1986.673099761952481801 CFX.

The program will use account1 to call contract Increment (1) initiates interaction with the contract. This function will add 1 to each element in the array. After calling, it is found that the account balance of account1 will change to 1986.360599761952433413 CFX.

That is to say, after this interaction between account1 and contract, the account deducts:
$$1986.673099761952481801-1986.360599761952433413=0.312500000000048388(CFX)‬$$

This means that by calling contract Increment (1) interacts with the contract, modifies the data and writes the log to the blockchain. account1 handed in CFX of 0.312500000000048388.

Use account2 to call the contract to help account1 free up space

The program will continue to run and use account2 by calling contract Increment (2) initiate interaction with the contract

Before calling, the account balance of account1 is 1986.360599761952433413 CFX, which is consistent with the account balance of account1 at the end of the previous operation.

After account2 calls the contract, the CFX balance of account1 becomes: 1986.673099761952433413 CFX

In other words, after the contract is called by account2, the CFX balance of account T1 changes to:
$$1986.360599761952433413-1986.673099761952433413=-0.3125(CFX)‬$$

This means that 0.3125 CFX will be returned to the account of account1 because the contract space of 320Bytes is released due to the change of account1 calling the increment function. According to the payment amount calculated in step (1): 0.06250000000002695 CFX, we can infer that account1 calls contract Increment (1) the actual cost consumed is:
$$0.312500000000048388-0.3125=0.000000000000048388(CFX)$$

Before account1 calls the contract in the program, the CFX balance of account1 displayed in the program is 1986.673099761952481801 CFX.

In the program, after account2 calls the contract, the CFX balance of account1 account is 1986.673099761952433413 CFX.

In other words, after this interaction with the contract, 0.000000000000048388 CFX were deducted from account1 account, and the calculation formula is as follows:
$$1986.673099761952481801-1986.673099761952433413=0.000000000000048388(CFX)$$

At this time, we go to confluxscan to check the balance corresponding to account T1: 1986.673099761952404514 CFX

w=559&h=159&f=png&s=23450)

According to the calculation formula:
$$1986.673099761952452902-1986.673099761952404514=0.000000000000048388 (CFX)‬$$
This also proves that account1 calls contract When the increment (1) interacts with the contract, its account actually consumes 0.000000000000048388 CFX

3. A struct sample containing string and uint (and uint is modified when called)

The contract code is as follows:

pragma solidity ^0.5.0;

contract Struct_test {

    struct Animal {
        string name;
        uint age;
    }
    event SelfEvent(address indexed sender,uint current);
    event SelfEvent_string(address indexed sender,string current);
    Animal animal1 = Animal("English short",5);
    Animal animal2 = Animal("Beauty short",5);
    
    
    function getAnimal(uint inc) public{
        animal1.age+=inc;
        animal2.age-=inc;
    }
    function get() public view returns(uint256){
        return animal1.age;
    }
    function self() public {
        emit SelfEvent(msg.sender, animal1.age);
        emit SelfEvent(msg.sender, animal2.age);
        emit SelfEvent_string(msg.sender, animal1.name);
        emit SelfEvent_string(msg.sender, animal2.name);
    }    
}

Because one of the smart contracts contains string name; And uint age; The structure variable Animal, where string corresponds to variable length array, the number of corresponding entries is subject to the actual setting, and uint corresponds to one entry. The getAnimal() function is invoked in the contract to modify the age variables of instantiated animal1 and animal2. The number of changes is 2.
$$CFS( α) ≡CFS( α;σ) ≡ account a in world status σ Total number of storage entries owned under ×\ left(\frac{1}{16}\right)(CFX)$$

Because animal1 age+=inc; And animal2 age-=inc; Two entries of 64B length are called and changed, and the changes are recorded into the blockchain. Combined with the example given before, the amount of pledge should be the age variable in the instance element of the changed structure, which exactly corresponds to two entries of 64B length. Since the name is not changed, combined with the example analysis given before, the amount of pledge should be 0.125 CFX
$$\left(\frac{1}{16}\right)×2=0.125(CFX)$$

The code for calling the contract is as follows:

const { Conflux, util } = require('js-conflux-sdk');
// This address is the receipt printed above contractCreated 
const public_address = '0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca';
const contractAddress = '0x84dd09cd48e07426c4ac50a389930c034be6c82a';
const PRIVATE_KEY1 = '0x2772b19636f1d183a9a2a0d27da2a1d0efb97637b425*';
const PRIVATE_KEY2 = '0x2adba218d5eacb5bc9bbb4c6fdecef7d1719c8184812*';
const compiled = require(`./build/Struct_test`)
async function main() {
  const cfx = new Conflux({
    url: 'http://main.confluxrpc.org',
  });
  const contract = cfx.Contract({
    address : contractAddress,
    abi: compiled.abi,
  });
  
  const before_call_balance = await cfx.getBalance(public_address);
  console.log("before account1 call the current drip:"+before_call_balance.toString());
  console.log("before account1 call the current cfx:"+util.unit.fromDripToCFX(before_call_balance));
  
  const account1 = cfx.Account(PRIVATE_KEY1);//Create account using private key
  
  // Record and spend CFX
  await contract.getAnimal(3).sendTransaction({ from: account1 }).confirmed();
  
  const after_call_balance = await cfx.getBalance(public_address);
  console.log("after account1 call getAnimal() the current drip:"+after_call_balance.toString());
  console.log("after account1 call getAnimal() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
    
  //Create account2 and try to call the contract to release cfx of account1
  const account2 = cfx.Account(PRIVATE_KEY2);//Create account using private key
  
  const before_account2_call_balance = await cfx.getBalance(public_address);
  console.log("before account2 call getAnimal() the current drip:"+after_call_balance.toString());
  console.log("before account2 call getAnimal() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  await contract.getAnimal(3).sendTransaction({ from: account2 }).confirmed();
  
  const after_account2_call_balance = await cfx.getBalance(public_address);
  console.log("after account2 call getAnimal() the current drip:"+after_account2_call_balance.toString());
  console.log("after account2 call getAnimal() the current cfx:"+util.unit.fromDripToCFX(after_account2_call_balance));
}
main().catch(e => console.error(e));

For the convenience of description, the accounts involved in calling the contract are represented by account1 and account2, and the called contract is represented by contract. The account information and the corresponding account address are summarized as follows:

Account nameAccount address
account10x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca
account20x1941E3137aDDf02514cBFeC292710463d41e8196
countract0x84dd09cd48e07426c4ac50a389930c034be6c82a

For convenience, the account name in the above table is used to refer to each account

Before calling, use confluxscan Check the CFX balance corresponding to account1 and find that the balance is 1983.472904449450987608 CFX.

(1) Use account1 for the first contract call:

Since the getAnimal() function called by account1 modifies the records stored in the contract, occupies the storage space, and the modified data is recorded by the blockchain, CFX needs to be submitted as a deposit

After the program starts: first display the account balance of account1: 1983.472904449451016507 CFX.

The program will use account1 to call contract Getanimal (3) initiates interaction with the contract, and this function will be applied to animal1 Add 3 to animal2 Age decreases by 3. After calling, it is found that the account balance of account1 will change to 1983.347904449450984359 CFX.

That is to say, after this interaction between account1 and contract, the account deducts:
$$1983.472904449451016507-1983.347904449450984359=0.125000000000032148(CFX)‬$$

This means that by calling contract Getanimal (3) interacts with the contract, modifies the data and writes the log to the blockchain. account1 handed in CFX with a quota of 0.125 000000000 32148.

Use account2 to call the contract to help account1 free up space

The program that invokes the contract will continue to run and use account2 by calling contract Getanimal (3) initiates interaction with the contract

Before calling, the account balance of account1 is 1983.347904449450984359 CFX, which is consistent with the account balance of account1 at the end of the previous operation.

After account2 invokes the contract, the CFX balance of account1 becomes 1983.472904449450984359 CFX

In other words, after the contract is called by account2, the CFX balance of account T1 changes to:
$$1983.347904449450984359-1983.472904449450984359=-0.125(CFX)‬$$

This means that the contract space of 128Bytes occupied by account1 calling getAnimal() function is released by account2 calling the contract, and 0.125 CFX will be returned to account1's account. According to the payment amount calculated in step (1): 0.125 000000000 32148 CFX, we can infer that account1 calls contract The actual cost of getanimal (3) is:
$$0.125000000000032148-0.125=0.000000000000032148(CFX)$$

Before account1 calls the contract in the program, the CFX balance of account1 displayed in the program is 1983.472904449451016507 CFX.

In the program, after account2 calls the contract, the CFX balance of account1 account is 1983.472904449450984359 CFX.

In other words, after this interaction with the contract, 0.000000000000032148 CFX were deducted from account1 account, and the calculation formula is as follows:
$$1986.673099761952481801-1986.673099761952433413=0.000000000000032148(CFX)$$

At this time, we go to confluxscan to check the balance corresponding to account T1: 1983.47290444945095546 CFX

According to the calculation formula:
$$1983.472904449450987608-1983.47290444945095546=0.000000000000032148 (CFX)‬$$
This also proves that account1 calls contract When getanimal (3) interacts with the contract, its account actually consumes 0.000000000000032148 CFX

4. An example with mapping

The contract code is as follows:

pragma solidity ^0.5.0;

contract mapping_test {
    mapping(address => uint) public balances;
    event SelfEvent(address indexed sender,uint current);
    function update(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }
    function self() public {
        emit SelfEvent(msg.sender, balances[msg.sender]);
    }    
}

The mapping structure is used in this contract because one of the smart contracts contains mapping (address = > uint) balance, In addition, when calling the relevant function, the data of the entry corresponding to address will be updated, one entry with a length of 64B will be called and changed, and the change will be recorded into the blockchain. Combined with the example given before, 0.0625 CFX should be pledged.

The calling contract code is as follows:

const { Conflux, util } = require('js-conflux-sdk');
// This address is the receipt printed above contractCreated 
const public_address = '0x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca';
const contractAddress = '0x8433f943dd6a4cbf13209b9e8674c08349872ce8';
const PRIVATE_KEY1 = '0x2772b19636f1d183a9a2a0d27da2a1d0efb977';
const PRIVATE_KEY2 = '0x2adba218d5eacb5bc9bbb4c6fdecef7d1719c';
const compiled = require(`./build/mapping_test`)
async function main() {
  const cfx = new Conflux({
    url: 'http://main.confluxrpc.org',
  });
  const contract = cfx.Contract({
    address : contractAddress,
    abi: compiled.abi,
  });
  
  const before_call_balance = await cfx.getBalance(public_address);
  console.log("before account1 call the current drip:"+before_call_balance.toString());
  console.log("before account1 call the current cfx:"+util.unit.fromDripToCFX(before_call_balance));
  
  const account1 = cfx.Account(PRIVATE_KEY1);//Create account using private key
  
  // Record and spend CFX
  await contract.update(3).sendTransaction({ from: account1 }).confirmed();
  
  const after_call_balance = await cfx.getBalance(public_address);
  console.log("after account1 call update() the current drip:"+after_call_balance.toString());
  console.log("after account1 call update() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
    
  //Create account2 and try to call the contract to release cfx of account1
  const account2 = cfx.Account(PRIVATE_KEY2);//Create account using private key
  
  const before_account2_call_balance = await cfx.getBalance(public_address);
  console.log("before account2 call update() the current drip:"+after_call_balance.toString());
  console.log("before account2 call update() the current cfx:"+util.unit.fromDripToCFX(after_call_balance));
  await contract.update(5).sendTransaction({ from: account2 }).confirmed();
  
  const after_account2_call_balance = await cfx.getBalance(public_address);
  console.log("after account2 call update() the current drip:"+after_account2_call_balance.toString());
  console.log("after account2 call update() the current cfx:"+util.unit.fromDripToCFX(after_account2_call_balance));
  
  
}
main().catch(e => console.error(e));

For the convenience of description, the account involved in calling the contract is represented by account1 and the called contract is represented by contract. The account information and the corresponding account address are summarized as follows:

Account nameAccount address
account10x17b38613e633c2b8fb4686a3a62b9b782ac5e0ca
account20x1941E3137aDDf02514cBFeC292710463d41e8196
countract0x8433f943dd6a4cbf13209b9e8674c08349872ce8

For convenience, the account name in the above table is used to refer to each account

The output when calling is as follows:

After the program starts: first display the account balance of account1: 98.955078124999570464 CFX.

The program will use account1 to call contract Update (5) initiates an interaction with the contract. This function will set a new value for balances[msg.sender]. After calling, it is found that the account balance of account1 will change to 98.89257812499543647 CFX.

That is to say, after this interaction between account1 and contract, the account deducts:
$$98.955078124999570464-98.892578124999543647=0.062500000000026817(CFX)‬$$

Since the entry for storing the mortgage is 1, the pledge of 0.0625 CFX is correct, and the call to the contract cost 0.000000000000026817 (CFX)

Topics: Blockchain