Skip to content

Commit

Permalink
Merge pull request #542 from protofire/gc-small-strings
Browse files Browse the repository at this point in the history
GC: small strings check
  • Loading branch information
dbale-altoros authored Jan 19, 2024
2 parents e6119ac + 7a974a1 commit dd64cde
Show file tree
Hide file tree
Showing 8 changed files with 530 additions and 3 deletions.
1 change: 1 addition & 0 deletions conf/rulesets/solhint-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module.exports = Object.freeze({
],
'constructor-syntax': 'warn',
'gas-multitoken1155': 'warn',
'gas-small-strings': 'warn',
'comprehensive-interface': 'warn',
quotes: ['error', 'double'],
'const-name-snakecase': 'warn',
Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ title: "Rule Index of Solhint"
| Rule Id | Error | Recommended | Deprecated |
| ------------------------------------------------------------------- | --------------------------------------------------- | ----------- | ---------- |
| [gas-multitoken1155](./rules/gas-consumption/gas-multitoken1155.md) | ERC1155 is a cheaper non-fungible token than ERC721 | | |
| [gas-small-strings](./rules/gas-consumption/gas-small-strings.md) | Keep strings smaller than 32 bytes | | |

## Miscellaneous
Expand Down
2 changes: 1 addition & 1 deletion docs/rules/gas-consumption/gas-multitoken1155.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This rule accepts a string option of rule severity. Must be one of "error", "war
```

### Notes
- [source](https://www.rareskills.io/post/gas-optimization?postId=c9db474a-ff97-4fa3-a51d-fe13ccb8fe3b&utm_campaign=42ccb5d8-c2cc-4416-b661-8eec8368f72b&utm_source=so&utm_medium=mail&utm_content=40a3d3be-d07d-479e-af1d-6b2ef1b950da&cid=9619984a-b43c-4002-ba71-820fd72bb83a#viewer-8v8t9) of the rule initiciative
- [source](https://www.rareskills.io/post/gas-optimization?postId=c9db474a-ff97-4fa3-a51d-fe13ccb8fe3b#viewer-8v8t9) of the rule initiciative

## Examples
This rule does not have examples.
Expand Down
38 changes: 38 additions & 0 deletions docs/rules/gas-consumption/gas-small-strings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
warning: "This is a dynamically generated file. Do not edit manually."
layout: "default"
title: "gas-small-strings | Solhint"
---

# gas-small-strings
![Category Badge](https://img.shields.io/badge/-Gas%20Consumption%20Rules-informational)
![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)

## Description
Keep strings smaller than 32 bytes

## Options
This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to warn.

### Example Config
```json
{
"rules": {
"gas-small-strings": "warn"
}
}
```

### Notes
- [source](https://www.rareskills.io/post/gas-optimization?postId=c9db474a-ff97-4fa3-a51d-fe13ccb8fe3b#viewer-ck1vq) of the rule initiciative

## Examples
This rule does not have examples.

## Version
This rule is introduced in the latest version.

## Resources
- [Rule source](https://github.com/protofire/solhint/tree/master/lib/rules/gas-consumption/gas-small-strings.js)
- [Document source](https://github.com/protofire/solhint/tree/master/docs/rules/gas-consumption/gas-small-strings.md)
- [Test cases](https://github.com/protofire/solhint/tree/master/test/rules/gas-consumption/gas-small-strings.js)
2 changes: 1 addition & 1 deletion lib/rules/gas-consumption/gas-multitoken1155.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const meta = {
category: 'Gas Consumption Rules',
notes: [
{
note: '[source](https://www.rareskills.io/post/gas-optimization?postId=c9db474a-ff97-4fa3-a51d-fe13ccb8fe3b&utm_campaign=42ccb5d8-c2cc-4416-b661-8eec8368f72b&utm_source=so&utm_medium=mail&utm_content=40a3d3be-d07d-479e-af1d-6b2ef1b950da&cid=9619984a-b43c-4002-ba71-820fd72bb83a#viewer-8v8t9) of the rule initiciative',
note: '[source](https://www.rareskills.io/post/gas-optimization?postId=c9db474a-ff97-4fa3-a51d-fe13ccb8fe3b#viewer-8v8t9) of the rule initiciative',
},
],
},
Expand Down
159 changes: 159 additions & 0 deletions lib/rules/gas-consumption/gas-small-strings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
const BaseChecker = require('../base-checker')

const ruleId = 'gas-small-strings'
const meta = {
type: 'gas-consumption',

docs: {
description: 'Keep strings smaller than 32 bytes',
category: 'Gas Consumption Rules',
notes: [
{
note: '[source](https://www.rareskills.io/post/gas-optimization?postId=c9db474a-ff97-4fa3-a51d-fe13ccb8fe3b#viewer-ck1vq) of the rule initiciative',
},
],
},

isDefault: false,
recommended: false,
defaultSetup: 'warn',

schema: null,
}

class GasSmallStrings extends BaseChecker {
constructor(reporter) {
super(reporter, ruleId, meta)
}

FileLevelConstant(node) {
let isLarger = false

if (node.typeName.name === 'string') {
isLarger = this.isStringLargerThan32Bytes(node.initialValue.value)
}

if (isLarger) {
this.reportError(node)
}
}

VariableDeclaration(node) {
let isLarger = false

if (node.parent.type === 'StateVariableDeclaration' && node.typeName.name === 'string') {
isLarger = this.isStringLargerThan32Bytes(node.expression.value)
}

if (
node.parent.type === 'StateVariableDeclaration' &&
node.typeName.type === 'ArrayTypeName' &&
node.typeName.baseTypeName.name === 'string'
) {
let isComponentLarger = false
const componentsSize = node.expression.components.length

let i = 0
while (i < componentsSize) {
isComponentLarger = this.isStringLargerThan32Bytes(node.expression.components[i].value)
i++
if (isComponentLarger) {
// update the result
isLarger = true
// if there is one larger this loop can be exited
i = componentsSize + 1
}
}
}

if (node.parent.type === 'VariableDeclarationStatement' && node.typeName.name === 'string') {
isLarger = this.isStringLargerThan32Bytes(node.parent.initialValue.value)
}

if (isLarger) {
this.reportError(node)
}
}

BinaryOperation(node) {
let isLarger = false

if (node.right.type === 'StringLiteral') {
isLarger = this.isStringLargerThan32Bytes(node.right.value)
}

if (isLarger) {
this.reportError(node)
}
}

FunctionCall(node) {
let isLarger = false
let isArgumentLarger = false
const argumentsSize = node.arguments.length

let i = 0
while (i < argumentsSize) {
if (node.arguments[i].type === 'StringLiteral') {
isArgumentLarger = this.isStringLargerThan32Bytes(node.arguments[i].value)
if (isArgumentLarger) {
// update the result
isLarger = true
// if there is one larger this loop can be exited
i = argumentsSize
}
}
i++
}

if (isLarger) {
this.reportError(node)
}
}

ReturnStatement(node) {
let isLarger = false
let isComponentLarger = false
const componentsSize = node.expression.components.length

let i = 0
while (i < componentsSize) {
if (
node.expression.components[i].type === 'StringLiteral'
// &&
// node.parent.parent.returnParameters[i].typeName.name === 'string'
) {
isComponentLarger = this.isStringLargerThan32Bytes(node.expression.components[i].value)
if (isComponentLarger) {
// update the result
isLarger = true
// if there is one larger this loop can be exited
i = componentsSize
}
}
i++
}

if (isLarger) {
this.reportError(node)
}
}

isStringLargerThan32Bytes(inputString) {
if (inputString.startsWith('0x') && inputString.length === 42) {
// Check if the input is a valid Ethereum address
return false
} else {
// Convert the string to Buffer and get its byte length
const byteLength = Buffer.from(inputString, 'utf8').length
// Check if the byte length is greater than 32
return byteLength > 32
}
}

reportError(node) {
this.error(node, 'GC: String exceeds 32 bytes')
}
}

module.exports = GasSmallStrings
8 changes: 7 additions & 1 deletion lib/rules/gas-consumption/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
const GasMultitoken1155 = require('./gas-multitoken1155')
const GasSmallStrings = require('./gas-small-strings')
// const GasIndexedEvents = require('./gas-indexed-events')

// module.exports = function checkers(reporter, config, tokens) {
module.exports = function checkers(reporter, config) {
return [new GasMultitoken1155(reporter, config)]
return [
new GasMultitoken1155(reporter, config),
new GasSmallStrings(reporter, config),
// new GasIndexedEvents(reporter, config),
]
}
Loading

0 comments on commit dd64cde

Please sign in to comment.