From 82106d46553d6f548f2792405992b1583bf2c311 Mon Sep 17 00:00:00 2001 From: wechman Date: Mon, 20 Jun 2022 09:22:59 +0200 Subject: [PATCH] Fix UTF-8 sequence validation when used with array literals. --- libsolidity/analysis/TypeChecker.cpp | 58 ++++++++++++++++++- libsolidity/analysis/TypeChecker.h | 1 + libsolidity/ast/ASTAnnotations.h | 3 + libsolidity/codegen/ExpressionCompiler.cpp | 13 ++++- .../codegen/ir/IRGeneratorForStatements.cpp | 21 ++++++- ...nup_during_multi_element_per_slot_copy.sol | 4 +- ...ion_type_inline_array_to_memory_array.sol} | 11 +--- .../function_implicit_conversion.sol | 41 +++++++++++++ .../skip_dynamic_types_for_structs.sol | 2 +- ..._of_string_literals_to_calldata_string.sol | 5 ++ ...line_array_with_invalid_utf8_sequences.sol | 8 +++ ...th_invalid_utf8_sequences_index_access.sol | 7 +++ ...ay_with_invalid_utf8_sequences_storage.sol | 5 ++ .../invalid_types_in_inline_array.sol | 2 + ...line_array_with_invalid_utf8_sequences.sol | 7 +++ ...th_invalid_utf8_sequences_index_access.sol | 7 +++ ...line_array_with_invalid_utf8_sequences.sol | 11 ++++ ...line_array_with_invalid_utf8_sequences.sol | 7 +++ 18 files changed, 195 insertions(+), 18 deletions(-) rename test/libsolidity/{syntaxTests/conversion/function_type_array_to_memory.sol => semanticTests/array/function_type_inline_array_to_memory_array.sol} (58%) create mode 100644 test/libsolidity/semanticTests/functionTypes/function_implicit_conversion.sol create mode 100644 test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences.sol create mode 100644 test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences_index_access.sol create mode 100644 test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences_storage.sol create mode 100644 test/libsolidity/syntaxTests/inline_arrays/multidimensional_inline_array_with_invalid_utf8_sequences.sol create mode 100644 test/libsolidity/syntaxTests/inline_arrays/nested_inline_array_with_invalid_utf8_sequences_index_access.sol create mode 100644 test/libsolidity/syntaxTests/inline_arrays/return_inline_array_with_invalid_utf8_sequences.sol create mode 100644 test/libsolidity/syntaxTests/inline_arrays/return_multidimensional_inline_array_with_invalid_utf8_sequences.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index ae27741a8..750e5689f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1229,6 +1229,15 @@ bool TypeChecker::visit(ForStatement const& _forStatement) return false; } +bool TypeChecker::visit(Return const& _return) +{ + if (_return.expression() && _return.annotation().functionReturnParameters) + for (auto parameter: _return.annotation().functionReturnParameters->parameters()) + if (parameter) + _return.expression()->annotation().resultExpectedTypes.push_back(parameter->type()); + return true; +} + void TypeChecker::endVisit(Return const& _return) { ParameterList const* params = _return.annotation().functionReturnParameters; @@ -1342,9 +1351,13 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) return false; } + vector> const& variables = _statement.declarations(); + for (auto const& variable: variables) + if (variable) + _statement.initialValue()->annotation().resultExpectedTypes.push_back(variable->type()); + // Here we have an initial value and might have to derive some types before we can visit // the variable declaration(s). - _statement.initialValue()->accept(*this); TypePointers valueTypes; if (auto tupleType = dynamic_cast(type(*_statement.initialValue()))) @@ -1352,7 +1365,6 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) else valueTypes = TypePointers{type(*_statement.initialValue())}; - vector> const& variables = _statement.declarations(); if (variables.empty()) // We already have an error for this in the SyntaxChecker. solAssert(m_errorReporter.hasErrors(), ""); @@ -1645,6 +1657,13 @@ bool TypeChecker::visit(TupleExpression const& _tuple) if (!components[i]) m_errorReporter.fatalTypeError(8381_error, _tuple.location(), "Tuple component cannot be empty."); + if (TupleExpression const* componentTupleExpression = dynamic_cast(components[i].get()); + componentTupleExpression && !_tuple.annotation().resultExpectedTypes.empty() && _tuple.isInlineArray()) + { + solAssert(_tuple.annotation().resultExpectedTypes.size() == 1); + componentTupleExpression->annotation().resultExpectedTypes = {dynamic_cast(_tuple.annotation().resultExpectedTypes.front())->baseType()}; + } + components[i]->accept(*this); types.push_back(type(*components[i])); @@ -1665,6 +1684,26 @@ bool TypeChecker::visit(TupleExpression const& _tuple) { solAssert(!!types[i], "Inline array cannot have empty components"); + if (_tuple.annotation().resultExpectedTypes.size()) + { + solAssert(_tuple.annotation().resultExpectedTypes.size() == 1); + ArrayType const* toArray = dynamic_cast(_tuple.annotation().resultExpectedTypes.front()); + Type const* componentType = type(*components[i]); + BoolResult result = componentType->isImplicitlyConvertibleTo(*toArray->baseType()); + + if (!result) + { + std::string const errorMessage = + "Type " + + componentType->toString(false) + + " is not implicitly convertible to expected type " + + toArray->baseType()->toString(false) + + (result.message().empty() ? "." : ". " + result.message()); + + m_errorReporter.typeError(6069_error, components[i]->location(), errorMessage); + } + } + if ((i == 0 || inlineArrayType) && !types[i]->mobileType()) m_errorReporter.fatalTypeError(9563_error, components[i]->location(), "Invalid mobile type."); @@ -1694,7 +1733,13 @@ bool TypeChecker::visit(TupleExpression const& _tuple) "Type " + inlineArrayType->toString(true) + " is only valid in storage." ); - _tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size()); + if (_tuple.annotation().resultExpectedTypes.size()) + { + solAssert(_tuple.annotation().resultExpectedTypes.size() == 1); + _tuple.annotation().type = TypeProvider::withLocationIfReference(DataLocation::Memory, _tuple.annotation().resultExpectedTypes.front()); + } + else + _tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size()); } else { @@ -3286,6 +3331,12 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) bool TypeChecker::visit(IndexAccess const& _access) { _access.annotation().isConstant = false; + + if (TupleExpression const* tuple = dynamic_cast(&_access.baseExpression())) + for (auto const& expectedType: _access.annotation().resultExpectedTypes) + _access.baseExpression().annotation().resultExpectedTypes.push_back( + TypeProvider::array(DataLocation::Memory, expectedType, tuple->components().size())); + _access.baseExpression().accept(*this); Type const* baseType = type(_access.baseExpression()); Type const* resultType = nullptr; @@ -3829,6 +3880,7 @@ Declaration const& TypeChecker::dereference(IdentifierPath const& _path) const bool TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) { + _expression.annotation().resultExpectedTypes = { &_expectedType }; _expression.accept(*this); BoolResult result = type(_expression)->isImplicitlyConvertibleTo(_expectedType); if (!result) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 2ef6b818b..ba4a3aa9c 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -144,6 +144,7 @@ private: void endVisit(TryStatement const& _tryStatement) override; bool visit(WhileStatement const& _whileStatement) override; bool visit(ForStatement const& _forStatement) override; + bool visit(Return const& _return) override; void endVisit(Return const& _return) override; void endVisit(EmitStatement const& _emit) override; void endVisit(RevertStatement const& _revert) override; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 3497a2ea8..7fbe9121a 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -287,6 +287,9 @@ struct ExpressionAnnotation: ASTAnnotation /// Note that even the simplest expressions, like `(f)()`, result in an indirect call even if they consist of /// values known at compilation time. bool calledDirectly = false; + + // Types that expression result is assigned to. + std::vector resultExpectedTypes; }; struct IdentifierAnnotation: ExpressionAnnotation diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 95087aa02..44c8093c7 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -368,11 +368,20 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple) if (_tuple.isInlineArray()) { ArrayType const& arrayType = dynamic_cast(*_tuple.annotation().type); + u256 memorySize = max(u256(32u), u256(_tuple.components().size() * arrayType.baseType()->memoryHeadSize())); - solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array."); - utils().allocateMemory(max(u256(32u), arrayType.memoryDataSize())); + if (arrayType.isDynamicallySized()) + memorySize += 32; + + utils().allocateMemory(memorySize); m_context << Instruction::DUP1; + if (arrayType.isDynamicallySized()) + { + m_context << u256(_tuple.components().size()); + utils().storeInMemoryDynamic(*TypeProvider::uint256()); + } + for (auto const& component: _tuple.components()) { acceptAndConvert(*component, *arrayType.baseType(), true); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 5eaeed743..dd3dfe7f8 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -495,7 +495,6 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) if (_tuple.isInlineArray()) { auto const& arrayType = dynamic_cast(*_tuple.annotation().type); - solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array."); define(_tuple) << m_utils.allocateMemoryArrayFunction(arrayType) << "(" << @@ -503,6 +502,19 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) ")\n"; string mpos = IRVariable(_tuple).part("mpos").name(); + + if (arrayType.isDynamicallySized()) + { + appendCode() << "// dupa\n"; + appendCode() << + m_utils.writeToMemoryFunction(*TypeProvider::uint256()) << + "(" << + (mpos + ", " + to_string(_tuple.components().size())) << + ")\n"; + } + + + Type const& baseType = *arrayType.baseType(); for (size_t i = 0; i < _tuple.components().size(); i++) { @@ -510,10 +522,15 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) component.accept(*this); setLocation(_tuple); IRVariable converted = convert(component, baseType); + + string position = + mpos + ", " + to_string(i * arrayType.memoryStride() + + (arrayType.isDynamicallySized() ? 32 : 0)); + appendCode() << m_utils.writeToMemoryFunction(baseType) << "(" << - ("add(" + mpos + ", " + to_string(i * arrayType.memoryStride()) + ")") << + ("add(" + position + ")") << ", " << converted.commaSeparatedList() << ")\n"; diff --git a/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol b/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol index 999d49eaa..960940905 100644 --- a/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol +++ b/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol @@ -16,7 +16,7 @@ contract C { } // ---- // constructor() -// gas irOptimized: 237351 +// gas irOptimized: 224415 // gas legacy: 221315 -// gas legacyOptimized: 185247 +// gas legacyOptimized: 208995 // f() -> 0 diff --git a/test/libsolidity/syntaxTests/conversion/function_type_array_to_memory.sol b/test/libsolidity/semanticTests/array/function_type_inline_array_to_memory_array.sol similarity index 58% rename from test/libsolidity/syntaxTests/conversion/function_type_array_to_memory.sol rename to test/libsolidity/semanticTests/array/function_type_inline_array_to_memory_array.sol index 179eafdc9..6b9f1777a 100644 --- a/test/libsolidity/syntaxTests/conversion/function_type_array_to_memory.sol +++ b/test/libsolidity/semanticTests/array/function_type_inline_array_to_memory_array.sol @@ -1,9 +1,7 @@ contract C { - function externalDefault() external returns(uint) { return 11; } function externalView() external view returns(uint) { return 12; } function externalPure() external pure returns(uint) { return 13; } - function internalDefault() internal returns(uint) { return 21; } function internalView() internal view returns(uint) { return 22; } function internalPure() internal pure returns(uint) { return 23; } @@ -41,9 +39,6 @@ contract C { } } // ---- -// TypeError 7407: (760-779): Type function () view external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory. -// TypeError 7407: (812-826): Type function () view returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory. -// TypeError 7407: (1230-1249): Type function () pure external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory. -// TypeError 7407: (1282-1296): Type function () pure returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory. -// TypeError 7407: (1688-1707): Type function () pure external returns (uint256)[1] memory is not implicitly convertible to expected type function () external returns (uint256)[1] memory. -// TypeError 7407: (1737-1751): Type function () pure returns (uint256)[1] memory is not implicitly convertible to expected type function () returns (uint256)[1] memory. +// testViewToDefault() -> 12, 22 +// testPureToDefault() -> 13, 23 +// testPureToView() -> 13, 23 diff --git a/test/libsolidity/semanticTests/functionTypes/function_implicit_conversion.sol b/test/libsolidity/semanticTests/functionTypes/function_implicit_conversion.sol new file mode 100644 index 000000000..8629c3fa0 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/function_implicit_conversion.sol @@ -0,0 +1,41 @@ +contract C { + function externalView() external view returns(uint) { return 12; } + function externalPure() external pure returns(uint) { return 13; } + + function internalView() internal view returns(uint) { return 22; } + function internalPure() internal pure returns(uint) { return 23; } + + function testViewToDefault() public returns (uint, uint) { + function () external returns(uint) externalDefault; + function () internal returns(uint) internalDefault; + + externalDefault = this.externalView; + internalDefault = internalView; + + return (externalDefault(), internalDefault()); + } + + function testPureToDefault() public returns (uint, uint) { + function () external returns(uint) externalDefault; + function () internal returns(uint) internalDefault; + + externalDefault = this.externalPure; + internalDefault = internalPure; + + return (externalDefault(), internalDefault()); + } + + function testPureToView() public returns (uint, uint) { + function () external view returns(uint) externalView; + function () internal view returns(uint) internalView; + + externalView = this.externalPure; + internalView = internalPure; + + return (externalView(), internalView()); + } +} +// ---- +// testViewToDefault() -> 12, 22 +// testPureToDefault() -> 13, 23 +// testPureToView() -> 13, 23 diff --git a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol index 0edb0e04e..b173c21b7 100644 --- a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol @@ -20,6 +20,6 @@ contract C { // ---- // g() -> 2, 6 -// gas irOptimized: 178637 +// gas irOptimized: 178634 // gas legacy: 180945 // gas legacyOptimized: 179460 diff --git a/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_array_of_string_literals_to_calldata_string.sol b/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_array_of_string_literals_to_calldata_string.sol index b9f95df66..52f94ac35 100644 --- a/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_array_of_string_literals_to_calldata_string.sol +++ b/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_array_of_string_literals_to_calldata_string.sol @@ -6,4 +6,9 @@ contract C { } } // ---- +// TypeError 6069: (123-126): Type literal_string "h" is not implicitly convertible to expected type string calldata. +// TypeError 6069: (128-131): Type literal_string "e" is not implicitly convertible to expected type string calldata. +// TypeError 6069: (133-136): Type literal_string "l" is not implicitly convertible to expected type string calldata. +// TypeError 6069: (138-141): Type literal_string "l" is not implicitly convertible to expected type string calldata. +// TypeError 6069: (143-146): Type literal_string "o" is not implicitly convertible to expected type string calldata. // TypeError 6359: (122-147): Return argument type string memory[5] memory is not implicitly convertible to expected type (type of first return variable) string calldata[5] calldata. diff --git a/test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences.sol new file mode 100644 index 000000000..1c1041987 --- /dev/null +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + string[2] memory a2 = [string(bytes(hex'74000001')), string(bytes(hex'c0a80101'))]; + string[2] memory a1 = [hex'74000001', hex'c0a80101']; + } +} +// ---- +// TypeError 6069: (182-195): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4. diff --git a/test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences_index_access.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences_index_access.sol new file mode 100644 index 000000000..52ddf6867 --- /dev/null +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences_index_access.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + string memory s = [hex'74000001', hex'c0a80101'][1]; + } +} +// ---- +// TypeError 6069: (86-99): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4. diff --git a/test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences_storage.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences_storage.sol new file mode 100644 index 000000000..307a4e194 --- /dev/null +++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_with_invalid_utf8_sequences_storage.sol @@ -0,0 +1,5 @@ +contract C { + string[2] data = [hex'74000001', hex'c0a80101']; +} +// ---- +// TypeError 6069: (50-63): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string storage ref. Contains invalid UTF-8 sequence at position 4. diff --git a/test/libsolidity/syntaxTests/inline_arrays/invalid_types_in_inline_array.sol b/test/libsolidity/syntaxTests/inline_arrays/invalid_types_in_inline_array.sol index 1ba1fdb1d..6c4dd79e8 100644 --- a/test/libsolidity/syntaxTests/inline_arrays/invalid_types_in_inline_array.sol +++ b/test/libsolidity/syntaxTests/inline_arrays/invalid_types_in_inline_array.sol @@ -4,4 +4,6 @@ contract C { } } // ---- +// TypeError 6069: (71-76): Type literal_string "foo" is not implicitly convertible to expected type uint256. +// TypeError 6069: (78-82): Type bool is not implicitly convertible to expected type uint256. // TypeError 6378: (66-83): Unable to deduce common type for array elements. diff --git a/test/libsolidity/syntaxTests/inline_arrays/multidimensional_inline_array_with_invalid_utf8_sequences.sol b/test/libsolidity/syntaxTests/inline_arrays/multidimensional_inline_array_with_invalid_utf8_sequences.sol new file mode 100644 index 000000000..2e7773d95 --- /dev/null +++ b/test/libsolidity/syntaxTests/inline_arrays/multidimensional_inline_array_with_invalid_utf8_sequences.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + string[2][2] memory a1 = [['foo', 'bar'], [hex'74000001', hex'c0a80101']]; + } +} +// ---- +// TypeError 6069: (110-123): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4. diff --git a/test/libsolidity/syntaxTests/inline_arrays/nested_inline_array_with_invalid_utf8_sequences_index_access.sol b/test/libsolidity/syntaxTests/inline_arrays/nested_inline_array_with_invalid_utf8_sequences_index_access.sol new file mode 100644 index 000000000..78e7fe755 --- /dev/null +++ b/test/libsolidity/syntaxTests/inline_arrays/nested_inline_array_with_invalid_utf8_sequences_index_access.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + string[2] memory s = [['foo', 'bar'], [hex'74000001', hex'c0a80101']][1]; + } +} +// ---- +// TypeError 6069: (106-119): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4. diff --git a/test/libsolidity/syntaxTests/inline_arrays/return_inline_array_with_invalid_utf8_sequences.sol b/test/libsolidity/syntaxTests/inline_arrays/return_inline_array_with_invalid_utf8_sequences.sol new file mode 100644 index 000000000..99a9201e1 --- /dev/null +++ b/test/libsolidity/syntaxTests/inline_arrays/return_inline_array_with_invalid_utf8_sequences.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure returns (string[2] memory) { + return [string(bytes(hex'74000001')), string(bytes(hex'c0a80101'))]; + } + + function g() public pure returns (string[2] memory) { + return [hex'74000001', hex'c0a80101']; + } +} +// ---- +// TypeError 6069: (244-257): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4. diff --git a/test/libsolidity/syntaxTests/inline_arrays/return_multidimensional_inline_array_with_invalid_utf8_sequences.sol b/test/libsolidity/syntaxTests/inline_arrays/return_multidimensional_inline_array_with_invalid_utf8_sequences.sol new file mode 100644 index 000000000..cfd5e17d2 --- /dev/null +++ b/test/libsolidity/syntaxTests/inline_arrays/return_multidimensional_inline_array_with_invalid_utf8_sequences.sol @@ -0,0 +1,7 @@ +contract C { + function g() public pure returns (string[2][2] memory) { + return [['foo', 'bar'], [hex'74000001', hex'c0a80101']]; + } +} +// ---- +// TypeError 6069: (122-135): Type literal_string hex"c0a80101" is not implicitly convertible to expected type string memory. Contains invalid UTF-8 sequence at position 4.