Solidity基础语法
环境准备
remix:https://remix.ethereum.org(在线IDE)
参考资料:https://github.com/inoutcode/ethereum_book(精通以太坊)
两种账户
CA:Contract Account,合约账户,没有私钥与之对应,合约就是⼀个CA,它也可以持有资⾦。
第⼀个dapp
1. 写状态变量(上链)是⼀笔交易(tx),需要矿⼯打包,所以需要花费资⾦(gas);
2. 读取状态变量,是从区块链中获取数据,不是⼀笔交易,所以免费。(必须加上view)
// 指定编译器版本,版本标识符(指该solidity文件能够被高于0.8.13和低于0.9.0版本的solidity编译。)
pragma solidity ^0.8.13;
// 关键字 contract 跟python、java的class⼀样
contract Inbox{
// 状态变量,存在链上
string public message;
// 构造函数
constructor(string memory initMessage) {
// 本地变量
string memory tmp = initMessage;
message = tmp;
}
// 写操作,需要⽀付gas
function setMessage(string memory _newMessage) public {
message = _newMessage;
}
// 读操作,不需要⽀付gas
function getMessage() public view returns(string memory) {
return message;
}
}
基础数据类型
- int(有符号整型,有正有负)int默认为int256
- uint(⽆符号整型,⽆负数)uint默认为uint256
- 以8位为区间,⽀持int8,int16,int24 ⾄ int256(uint同理)
- bool类型:true,false
- 定⻓字节:bytes1~bytes32
- 地址:address(20个字节,40个16进制字符,共160位),如:0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Primitives {
bool public flag = true;
/*
uint stands for unsigned integer, meaning non negative integers(代表无符号整数,即正数)
different sizes are available(各自的取值范围)
uint8 ranges from 0` to 2 ** 8 - 1 (0到2**8-1)
uint16 ranges from 0 to 2 ** 16 - 1(0到2**16-1)
...
uint256 ranges from 0 to 2 ** 256 - 1(0到2**256-1)
*/
uint8 public u8 = 1;
uint public u256 = 456;
uint public u = 123; // uint is an alias for uint256
/*
Negative numbers are allowed for int types.(int类型包含负数)
Like uint, different ranges are available from int8 to int256
int256 ranges from -2 ** 255 to 2 ** 255 - 1
int128 ranges from -2 ** 127 to 2 ** 127 - 1
*/
int8 public i8 = -1;
int public i256 = 456;
int public i = -123; // int is same as int256
// minimum and maximum of int
int public minInt = type(int).min;
int public maxInt = type(int).max;
address public addr = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;
/*In Solidity, the data type byte represent a sequence of bytes.
Solidity presents two type of bytes types :
- fixed-sized byte arrays
- dynamically-sized byte arrays.
The term bytes in Solidity represents a dynamic array of bytes.
It’s a shorthand for byte[] .
*/
bytes1 a = 0xb5; // [10110101]
bytes1 b = 0x56; // [01010110]
// Default values 初始值
// Unassigned variables have a default value
bool public defaultBoo; // false 默认为false
uint public defaultUint; // 0 默认为0
int public defaultInt; // 0 默认为0
address public defaultAddr; // 0x0000000000000000000000000000000000000000
}
变量variables
- 状态变量(state)
- 定义在合约内,函数外
- 存储在链上
- 本地变量(local)
- 定义在函数内
- 不会存储在链上
- 全局变量(global)
- 与当前合约⽆关,描述整个区块链的信息(时间、块⾼等)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Variables {
// State variables are stored on the blockchain.
string public msg = "Hello";
uint public age = 26;
function test() public {
// Local variables are not saved to the blockchain.
uint i = 456;
// Here are some global variables
uint height = block.blocks; // Current block height
address sender = msg.sender; // address of the caller
}
}
函数 |
含义 |
备注 |
blockhash(uint |
(byte32)哈希值 |
|
blockNumber) |
(byte32)哈希值 |
|
block.coinbase |
(address) 当前块矿⼯的地址 |
|
block.difficulty |
(uint)当前块的难度 |
|
block.gaslimit |
(uint)当前块的gaslimit |
|
block.number |
(uint)当前区块的块号 |
|
block.timestamp |
(uint)当前块的时间戳 |
常⽤ |
gasleft() |
(uint)当前还剩的gas |
|
tx.origin |
(address)交易的原始发送者的地址,只能是EOA |
常⽤ |
msg.sender |
(address)当前调⽤发起⼈的地址(可能是合约CA,也可能是EOA) |
常⽤ |
msg.sig |
(bytes4)调⽤数据的前四个字节(函数标识符) |
常⽤ |
msg.value |
(uint)这个消息所附带的货币量,单位为wei |
常⽤ |
msg.data |
(bytes)完整的调⽤数据(calldata) |
常⽤ |
tx.gasprice |
(uint) 交易的gas价格 |
|
常量constant
- 常量与变量相对,需要硬编码在合约中,合约部署之后,⽆法改变。
- 常量更加节约gas,⼀般⽤⼤写来代表常量。
- ⾼阶⽤法:clone合约时,如果合约内有初始值,必须使⽤constant,否则clone的新合约初始值为空值。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Constants {
// coding convention to uppercase constant variables
address public constant MY_ADDRESS = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
uint public constant MY_UINT = 123;
}
不可变量immutable
- 与常量类似,但是不必硬编码,可以在构造函数时传值,部署后⽆法改变。
- immutable仅⽀持值类型(如:int,address,bytes8),不⽀持⾮值类型(如:string,bytes)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Immutable {
// coding convention to uppercase constant variables
address public immutable MY_ADDRESS;
uint public immutable MY_UINT;
bytes1 public immutable MY_BYTES1 = 0xff;
// string public immutable greetings = "hello"; // error
constructor(uint _myUint) {MY_ADDRESS = msg.sender;MY_UINT = _myUint;
}
}
ether和wei
- 常⽤单位为:wei,gwei,ether
- 不含任何后缀的默认单位是 wei
- 1 gwei = 10^9 wei
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract EtherUnits {
uint public oneWei = 1 wei;
// 1 wei is equal to 1
bool public isOneWei = 1 wei == 1;
uint public oneEther = 1 ether;
// 1 ether is equal to 10^18 wei
bool public isOneEther = 1 ether == 1e18;
}
msg三⼈组
当⽤户发起⼀笔交易时,相当于向合约发送⼀个消息(msg),这笔交易可能会涉及到三个重要的全局变量,具体如下:
- msg.sender:表示这笔交易的调⽤者是谁(地址),同⼀个交易,不同的⽤户调⽤,msg.sender不同;
- msg.value:表示调⽤这笔交易时,携带的ether数量,这些以太坊由msg.sender⽀付,转⼊到当前合约
(wei单位整数);
- 注意:⼀个函数(或地址)如果想接收ether,需要将其修饰为:payable。
- msg.data:表示调⽤这笔交易的信息,由函数签名和函数参数(16进制字符串),组成代理模式时常⽤。
msg.sender
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract MsgSender {
address public owner;
uint256 public value;
address public caller;
constructor() {
//在部署合约的时候,设置⼀个全局唯⼀的合约所有者,后⾯可以使⽤权限控制
owner = msg.sender;
}
//1. 对与合约⽽⾔,msg.sender是⼀个可以改变的值,并不⼀定是合约的创造者
//2. 任何⼈调⽤了合约的⽅法,那么这笔交易中的from就是当前合约中的msg.sender
function setValue(uint256 input) public {
value = input;
caller = msg.sender;
}
}
msg.value
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract MsgValue {
// uint256 public money;
mapping(address=> uint256) public personToMoney;
// 函数⾥⾯使⽤了msg.value,那么函数要修饰为payable
function play() public payable {
// 如果转账不是100wei,那么参与失败
// 否则成功,并且添加到维护的mapping中
require(msg.value == 100, "should equal to 100!");
personToMoney[msg.sender] = msg.value;
}
// 查询当前合约的余额
function getBalance() public view returns(uint256) {
return address(this).balance;
}
}
msg.data
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract MsgData {
event Data(bytes data, bytes4 sig);
// input0: addr: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
// input1: amt : 1
function transfer(address addr, uint256 amt) public {
bytes memory data = msg.data;
// msg.sig 表示当前⽅法函数签名(4字节)
// msg.sig 等价于 this.transfer.selector
emit Data(data, msg.sig);
}
/*output:
data:0xa9059cbb0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000001
sig: 0xa9059cbb
对data进⾏分析:
0xa9059cbb //前四字节,函数名keccak256
0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4 //第⼀个参数占位符(32字节)
0000000000000000000000000000000000000000000000000000000000000001 //第⼆个参数占位符(32字节)*/
}
payable
- ⼀个函数(或地址)如果想接收ether,需要将其修饰为:payable。
- address常⽤⽅法:
- balance(): 查询当前地址的ether余额
- transfer(uint): 合约向当前地址转指定数量的ether,如果失败会回滚
- send(uint): 合约向当前地址转指定数量的ether,如果失败会返回false,不回滚(不建议使⽤send)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Payable {
// 1. Payable address can receive Ether
address payable public owner;
// 2. Payable constructor can receive Ether
constructor() payable {
owner = payable(msg.sender);
}
// 3. Function to deposit Ether into this contract.
function deposit() public payable {}
// 4. Call this function along with some Ether.
// The function will throw an error since this function is not payable.
function notPayable() public {}
// 5. Function to withdraw all Ether from this contract.
function withdraw() public {
uint amount = address(this).balance;
owner.transfer(amount);
}
// 6. Function to transfer Ether from this contract to address from input
function transfer(address payable _to, uint _amount) public {
_to.transfer(_amount);
}
}
gas相关
gas描述执⾏⼀笔交易时需要花费多少ether!(1 ether = 10^18wei)
交易⼿续费 = gas_used * gas_price,其中:
- gas:是数量单位,uint
- gas_used:表示⼀笔交易实际消耗的gas数量
- gas_price:每个gas的价格,单位是wei或gwei
- gas limit:表示你允许这⼀笔交易消耗的gas上限,⽤户⾃⼰设置(防⽌因为bug导致的损失)
- 如果gas_used⼩于gas_limit,剩余gas会返回给⽤户,这个值不再合约层⾯设置,在交易层⾯设置(如metamask)
- 如果gas_used⼤于gas_limit,交易失败,资⾦不退回
- block gas limit:表示⼀个区块能够允许的最⼤gas数量,由区块链⽹络设置
view和pure
view和pure⽤于修饰Getter函数(只读取数据的函数),其中:
- view:表示函数中不会修改状态变量,只是读取;
- pure:表示函数中不会使⽤状态变量,既不修改也不读取。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract ViewAndPure {
uint public x = 1;
// Promise not to modify the state.
function addToX(uint y) public view returns (uint) {
return x + y;
}
// Promise not to modify or read from the state.
function add(uint i, uint j) public pure returns (uint) {
return i + j;
}
}
bytes和string
byteN、bytes、string直接的关系
bytes:
- bytes是动态数组,相当于byte数组(如:byte[10])
- ⽀持push⽅法添加
- 可以与string相互转换
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Bytes {
bytes public name;
//1. 获取字节⻓度
function getLen() public view returns(uint256) {
return name.length;
}
//2. 可以不分空间,直接进⾏字符串赋值,会⾃动分配空间
function setValue(bytes memory input) public {
name = input;
}
//3. ⽀持push操作,在bytes最后⾯追加元素
function pushData() public {
name.push("h");
}
}
string:
- string 动态尺⼨的UTF-8编码字符串,是特殊的可变字节数组
- string 不⽀持下标索引、不⽀持length、push⽅法
- string 可以修改(需通过bytes转换)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract String {
string public name = "lily";
function setName() public {
bytes(name)[0] = "L";
}
function getLength() public view returns(uint256) {
return bytes(name).length;
}
}
struct
- ⾃定义结构类型,将不同的数据类型组合到⼀个结构中,⽬前⽀持参数传递结构体。
- 枚举和结构体都可以定义在另外⼀个⽂件中,进⾏import后使⽤
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Todos {
struct Todo {
string text;
bool completed;
}
// An array of 'Todo' structs
Todo[] public todos;
// [["hello", true], ["world", false]]
function createByElement(Todo[] memory _todos) public {
for (uint i = 0; i < _todos.length; i++) {
todos.push(_todos[i]);
}
}
function create(string memory _text) public {
// 3 ways to initialize a struct
// - calling it like a function
todos.push(Todo(_text, false));
// key value mapping
todos.push(Todo({text: _text, completed: false}));
// initialize an empty struct and then update it
Todo memory todo;
todo.text = _text;
// todo.completed initialized to false
todos.push(todo);
}
// Solidity automatically created a getter for 'todos' so
// you don't actually need this function.
function get(uint _index) public view returns (string memory text, bool completed){
Todo storage todo = todos[_index];
return (todo.text, todo.completed);
}
// update text
function update(uint _index, string memory _text) public {
Todo storage todo = todos[_index];
todo.text = _text;
}
// update completed
function toggleCompleted(uint _index) public {
Todo storage todo = todos[_index];
todo.completed = !todo.completed;
}
}
mapping
- 定义:mapping(keyType => valueType) myMapping
- key可以是任意类型,value可以是任意类型(value也可以是mapping或者数组)
- mapping不⽀持迭代器
- 不需要实例化等,定义后直接可以使⽤
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Mapping {
// Mapping from address to uint
mapping(address => uint) public myMap;
function get(address _addr) public view returns (uint) {
// Mapping always returns a value.
// If the value was never set, it will return the default value.
return myMap[_addr];
}
function set(address _addr, uint _i) public {
// Update the value at this address
myMap[_addr] = _i;
}
function remove(address _addr) public {
// Reset the value to the default value.
delete myMap[_addr];
}
}
contract NestedMapping {
// Nested mapping (mapping from address to another mapping)
mapping(address => mapping(uint => bool)) public nested;
function get(address _addr1, uint _i) public view returns (bool) {
// You can get values from a nested mapping
// even when it is not initialized
return nested[_addr1][_i];
}
function set(address _addr1,uint _i,bool _boo) public {
nested[_addr1][_i] = _boo;
}
function remove(address _addr1, uint _i) public {
delete nested[_addr1][_i];
}
}
修饰器modifier
修饰器⽤于修饰函数,在函数执⾏前或执⾏后进⾏调⽤,经常⽤于:
- 权限控制
- 参数校验
- 防⽌重⼊攻击等
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract FunctionModifier {
// We will use these variables to demonstrate how to use modifiers.
address public owner;
uint public x = 10;
bool public locked;
constructor() {
// Set the transaction sender as the owner of the contract.
owner = msg.sender;
}
// 1. Modifier to check that the caller is the owner of the contract.
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
// Underscore is a special character only used inside
// a function modifier and it tells Solidity to
// execute the rest of the code.
_;
}
// 2. Modifiers can take inputs. This modifier checks that the
// address passed in is not the zero address.
modifier validAddress(address _addr) {
require(_addr != address(0), "Not valid address");
_;
}
function changeOwner(address _newOwner) public onlyOwner validAddress(_newOwner) {
owner = _newOwner;
}
// Modifiers can be called before and / or after a function.
// This modifier prevents a function from being called while
// it is still executing.
modifier noReentrancy() {
require(!locked, "No reentrancy");
locked = true;
_;
locked = false;
}
function decrement(uint i) public noReentrancy {
x -= i;
if (i > 1) {
decrement(i - 1);
}
}
}
事件Event
事件是区块链上的⽇志,每当⽤户发起操作的时候,可以发送相应的事件,常⽤于:
- 监听⽤户对合约的调⽤
- 便宜的存储(⽤合约存储更加昂贵)
通过链下程序(如:subgraph)对合约进⾏事件监听,可以对Event进⾏搜集整理,从⽽做好数据统计,常⽤⽅式:
- 合约触发后发送事件
- subgraph对合约事件进⾏监听,计算(如:统计⽤户数量)
- 前端程序直接访问subgraph的服务,获得统计数据(这避免了在合约层⾯统计数据的费⽤,并且获取速度更快)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Event {
// Event declaration
// Up to 3 parameters can be indexed.
// Indexed parameters helps you filter the logs by the indexed parameter
event Log(address indexed sender, string message); // 修饰为indexed
event AnotherLog(); // ⽆参数的事件
event TestAnonymous(address indexed sender, uint256 num) anonymous; // 匿名事件
function test() public {
emit Log(msg.sender, "Hello World!");
emit Log(msg.sender, "Hello EVM!");
emit AnotherLog();
}
}
可⻅性visibility
合约的⽅法和状态变量需要使⽤关键字进⾏修饰,从⽽决定其是否可以被其他合约调⽤,修饰符包括:
- public:所有的合约和外部账户(EOA)都可以调⽤;
- private:只允许合约内部调⽤;
- internal:仅允许合约内部以及⼦合约中调⽤;
- external:仅允许外部地址(EOA或CA)调⽤,合约内部及⼦合约都不能调⽤;(早期版本可以使⽤this调⽤external⽅法)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Base {
// Private function can only be called
// - inside this contract
// Contracts that inherit this contract cannot call this function.
function privateFunc() private pure returns (string memory) {
return "private function called";
}
function testPrivateFunc() public pure returns (string memory) {
return privateFunc();
}
// Internal function can be called
// - inside this contract
// - inside contracts that inherit this contract
function internalFunc() internal pure returns (string memory) {
return "internal function called";
}
function testInternalFunc() public pure virtual returns (string memory) {
return internalFunc();
}
// Public functions can be called
// - inside this contract
// - inside contracts that inherit this contract
// - by other contracts and accounts
function publicFunc() public pure returns (string memory) {
return "public function called";
}
// External functions can only be called
// - by other contracts and accounts
function externalFunc() external pure returns (string memory) {
return "external function called";
}
// This function will not compile since we're trying to call
// an external function here.
// function testExternalFunc() public pure returns (string memory) {
// return externalFunc();
// }
// State variables
string private privateVar = "my private variable";
string internal internalVar = "my internal variable";
string public publicVar = "my public variable";
// State variables cannot be external so this code won't compile.
// string external externalVar = "my external variable";
}
contract Child is Base {
// Inherited contracts do not have access to private functions
// and state variables.
// function testPrivateFunc() public pure returns (string memory) {
// return privateFunc();
// }
// Internal function call be called inside child contracts.
function testInternalFunc() public pure override returns (string memory) {
return internalFunc();
}
}
ERC20(标准Token)
- EIP: Ethereum Improvement Propose
- 任何遵从EIP-20协议(ERC20标准)的Contract都属于ERC20 Token
标准接⼝
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
interface IERC20{
function totalSupply() public view returns (uint256)
function balanceOf(address _owner) public view returns (uint256 balance)
function transfer(address _to, uint256 _value) public returns (bool success)
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
function approve(address _spender, uint256 _value) public returns (bool success)
function allowance(address _owner, address _spender) public view returns (uint256 remaining)
// 2 REQUIRED EVENTS
event Transfer(address indexed _from, address indexed _to, uint256 _value)
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
// 3. OPTIONAL FUNCTIONS
function name() public view returns (string)
function symbol() public view returns (string)
function decimals() public view returns (uint8)
}
发⾏Token
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
// https://github.com/OpenZeppelin/openzeppelincontracts/blob/v3.0.0/contracts/token/ERC20/IERC20.sol
interface IERC20 {
// REQUIRED FUNCTIONS
// 总发⾏量
function totalSupply() external view returns (uint); // -> 总发⾏量 100000000 *10**decimals
// 标准decimal: 18
// USDT: 6
// WBTC: 8
function balanceOf(address account) external view returns (uint); // 指定账户的余额
// 币的持有⼈直接调⽤,进⾏转账
function transfer(address recipient, uint amount) external returns (bool);
// 最常⽤的!!
// 1. 我这个owner对合约进⾏approve,此时approve内部会修改allowance变量
// 2. 合约内部调⽤transferFrom来⽀配owner的token
function transferFrom( // spender就是这个合约
address sender, // owner
address recipient, // 转给谁
uint amount // ⾦额
) external returns (bool);
// owner: 币的持有⼈
// spender: 是指定帮助花费的代理⼈(被授权的⼈)
function allowance(address owner, address spender) external view returns (uint); //授权的额度
// decimals view,这是⼀个public 的变量,⾃动提供了⼀个读取的⽅法 // 返回精度
// 持有⼈对spender进⾏授权,在approve内部,会调⽤msg.sender来知道owner是谁
function approve(address spender, uint amount) external returns (bool);
// 2 REQUIRED EVENTS
// 事件
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
// 3. OPTIONAL FUNCTIONS
function name() public view returns (string)
function symbol() public view returns (string)
function decimals() public view returns (uint8)
}
以下是ERC20的案例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "./IERC20.sol";
contract ERC20 is IERC20 {
uint public totalSupply;
mapping(address => uint) public balanceOf;
mapping(address => mapping(address => uint)) public allowance;
string public name = "Solidity by Example";
string public symbol = "SOLBYEX";
uint8 public decimals = 18;
function transfer(address recipient, uint amount) external returns (bool) {
balanceOf[msg.sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
return true;
}
function approve(address spender, uint amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool) {
allowance[sender][msg.sender] -= amount;
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(sender, recipient, amount);
return true;
}
function mint(uint amount) external {
balanceOf[msg.sender] += amount;
totalSupply += amount;
emit Transfer(address(0), msg.sender, amount);
}
function burn(uint amount) external {
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
emit Transfer(msg.sender, address(0), amount);
}
}
可以使⽤openzeppelin库进⾏创建⾃⼰的token:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
// import "https://github.com/OpenZeppelin/openzeppelincontracts/blob/v4.0.0/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
// Mint 100 tokens to msg.sender
// Similar to how
// 1 dollar = 100 cents
// 1 token = 1 * (10 ** decimals)
_mint(msg.sender, 100 * 10**uint(decimals()));
}
// 默认是18,可以进⾏override
function decimals() public view override returns (uint8) {
return 6;
}
}
