Array as function parameter to mapping variable - ethereum

Suppose a function that accepts an array of addresses and looks like this:
function setVoters(address[] _inputAddresses) public ownerOnly {
// [...]
}
the same contract that uses the aforementioned function has a variable defined as a mapping:
mapping(address => bool) voter;
Is looping over the array and pushing it to the mapping considered the best option when it comes to gas consumption/expenses or would it be better if the function accepts one address and does the iteration from a given UI via some JavaScript functionality?
option a:
function setVoters(address[] _inputAddresses) public ownerOnly {
// [...]
for (uint index = 0; index < _inputAddresses.length; index++) {
voter[_inputAddresses[index]] = true;
}
}
vs
option b:
function setVoter(address _inputAddress) public ownerOnly {
// [...]
voter[_inputAddress] = true;
}
JavaScript would look like this
// loop condition starts here
await task.methods.setVoter(address[key]).send({
from: accounts[0]
});
// loop condition ends here

The best in terms of gas efficiency is option a, calling a function takes quite a bit of gas, so you would pay less if you did it all in one big tx rather than many small ones.

Related

How to return "Null" or an "Empty" object in Solidity?

I am currently writing a Smart Contract in Solidity. The smart contract, amongst other information, stores an array of properties object at the general level. The property object Looks like this:
struct PropertyObj {
string id;
uint weiPrice;
address owner;
}
Now there is a specific function that iterates over the array, finds the property and returns it (code below)
function getPropertyByid(string memory _propertyId)private view returns(PropertyObj memory){
for(uint i = 0; i<PropertyArray.length; i++){
if (keccak256(bytes((PropertyArray[i].id))) == keccak256(bytes((_propertyId)))) {
return PropertyArray[i];
}
return null;
}
}
The "Problem" is that, unlike other programming languages, Solidity does not allow to return null (as far as I am concerned).
In other words, if throughout the iteration we do not find the property, then what we shall return if we specified that we need to return PropertyObj memory in the function signature?
Solidity does not have null value, as you're correctly stating.
Your function can throw an exception using the revert() function.
It also seems that your implementation has a logical error. Your example would "return null" if the hash was not found during the first iteration. Instead, you may want to throw the exception after the loop has ended.
for(uint i = 0; i<PropertyArray.length; i++){
if (keccak256(bytes((PropertyArray[i].id))) == keccak256(bytes((_propertyId)))) {
return PropertyArray[i];
}
}
revert('Not found');
Other option would be to return the empty object (with default values, i.e. zeros), if it fits your use case.
for(uint i = 0; i<PropertyArray.length; i++) {
// ...
}
// not found, return empty `PropertyObj`
PropertyObj memory emptyPropertyObj;
return emptyPropertyObj;

how to return loop and array element | Data location must be "memory" for return parameter in function | Dynamic Array

I am using the following code to return array.
function getActiveDepositIndexes() public view returns (uint256 [] storage) {
User storage user = users[msg.sender];
Deposit[] storage deposits = user.deposits;
uint[] memory indices = new uint[](deposits.length);
for(uint i = 0; i < deposits.length; i++) {
if(deposits[i].active && !deposits[i].closed){
indices.push(i);
}
}
return indices;
}
But I am getting following error,
TypeError: Data location must be "memory" for return parameter in function, but "storage" was given.
function getActiveDepositIndexes() public view returns (uint256 [] storage) {
^----------------^
Environment:
Truffle v5.1.20 (core: 5.1.20)
Solidity - 0.6.0 (solc-js)
Node v8.16.2
Web3.js v1.2.1
Seems like you explicitly specify storage for function return value. Try replacing your function signature with the following:
function getActiveDepositIndexes() public view returns (uint[]) {...}
memory is used for return values by default
Use the following code instead:
function getActiveDepositIndexes() public view returns (uint256[] memory) {
User storage user = users[msg.sender];
Deposit[] storage deposits = user.deposits;
uint[] memory indices = new uint[](deposits.length);
for(uint i = 0; i < deposits.length; i++) {
if(deposits[i].active && !deposits[i].closed){
indices[i] = i;
}
}
return indices;
}
Issues with such logic on EVM is that there is no way of returning dynamic sized memory arrays and hence push() is also not usuable with memory elements. This workaround in the above example results in zero (placeholder) alues at inactive indices according to your code...

Return multiple values from function

Is there a way to return several values in a function return statement (other than returning an object) like we can do in Go (or some other languages)?
For example, in Go we can do:
func vals() (int, int) {
return 3, 7
}
Can this be done in Dart? Something like this:
int, String foo() {
return 42, "foobar";
}
Dart doesn't support multiple return values.
You can return an array,
List foo() {
return [42, "foobar"];
}
or if you want the values be typed use a Tuple class like the package https://pub.dartlang.org/packages/tuple provides.
See also either for a way to return a value or an error.
I'd like to add that one of the main use-cases for multiple return values in Go is error handling which Dart handle's in its own way with Exceptions and failed promises.
Of course this leaves a few other use-cases, so let's see how code looks when using explicit tuples:
import 'package:tuple/tuple.dart';
Tuple2<int, String> demo() {
return new Tuple2(42, "life is good");
}
void main() {
final result = demo();
if (result.item1 > 20) {
print(result.item2);
}
}
Not quite as concise, but it's clean and expressive code. What I like most about it is that it doesn't need to change much once your quick experimental project really takes off and you start adding features and need to add more structure to keep on top of things.
class FormatResult {
bool changed;
String result;
FormatResult(this.changed, this.result);
}
FormatResult powerFormatter(String text) {
bool changed = false;
String result = text;
// secret implementation magic
// ...
return new FormatResult(changed, result);
}
void main() {
String draftCode = "print('Hello World.');";
final reformatted = powerFormatter(draftCode);
if (reformatted.changed) {
// some expensive operation involving servers in the cloud.
}
}
So, yes, it's not much of an improvement over Java, but it works, it is clear, and reasonably efficient for building UIs. And I really like how I can quickly hack things together (sometimes starting on DartPad in a break at work) and then add structure later when I know that the project will live on and grow.
Create a class:
import 'dart:core';
class Tuple<T1, T2> {
final T1 item1;
final T2 item2;
Tuple({
this.item1,
this.item2,
});
factory Tuple.fromJson(Map<String, dynamic> json) {
return Tuple(
item1: json['item1'],
item2: json['item2'],
);
}
}
Call it however you want!
Tuple<double, double>(i1, i2);
or
Tuple<double, double>.fromJson(jsonData);
You can create a class to return multiple values
Ej:
class NewClass {
final int number;
final String text;
NewClass(this.number, this.text);
}
Function that generates the values:
NewClass buildValues() {
return NewClass(42, 'foobar');
}
Print:
void printValues() {
print('${this.buildValues().number} ${this.buildValues().text}');
// 42 foobar
}
The proper way to return multiple values would be to store those values in a class, whether your own custom class or a Tuple. However, defining a separate class for every function is very inconvenient, and using Tuples can be error-prone since the members won't have meaningful names.
Another (admittedly gross and not very Dart-istic) approach is try to mimic the output-parameter approach typically used by C and C++. For example:
class OutputParameter<T> {
T value;
OutputParameter(this.value);
}
void foo(
OutputParameter<int> intOut,
OutputParameter<String>? optionalStringOut,
) {
intOut.value = 42;
optionalStringOut?.value = 'foobar';
}
void main() {
var theInt = OutputParameter(0);
var theString = OutputParameter('');
foo(theInt, theString);
print(theInt.value); // Prints: 42
print(theString.value); // Prints: foobar
}
It certainly can be a bit inconvenient for callers to have to use variable.value everywhere, but in some cases it might be worth the trade-off.
Dart is finalizing records, a fancier tuple essentially.
Should be in a stable release a month from the time of writing.
I'll try to update, it's already available with experiments flags.
you can use dartz package for Returning multiple data types
https://www.youtube.com/watch?v=8yMXUC4W1cc&t=110s
you can use Set<Object> for returning multiple values,
Set<object> foo() {
return {'my string',0}
}
print(foo().first) //prints 'my string'
print(foo().last) //prints 0
In this type of situation in Dart, an easy solution could return a list then accessing the returned list as per your requirement. You can access the specific value by the index or the whole list by a simple for loop.
List func() {
return [false, 30, "Ashraful"];
}
void main() {
final list = func();
// to access specific list item
var item = list[2];
// to check runtime type
print(item.runtimeType);
// to access the whole list
for(int i=0; i<list.length; i++) {
print(list[i]);
}
}

Test if a Dart value is actually a function?

Is it possible to test if a value is a function that can be called? I can test for null easily but after that I have no idea how to ensure the parameter passed in is actually a function?
void myMethod(funcParam)
{
if (funcParam != null)
{
/* How to test if funcParam is actually a function that can be called? */
funcParam();
}
}
void myMethod(funcParam) {
if(funcParam is Function) {
funcParam();
}
}
Of course, the call to funcParams() only works if the parameter list matches - is Function doesn't check for that. If there are parameters involved, one can use typedefs to ensure this.
typedef void MyExpectedFunction(int someInt, String someString);
void myMethod(MyExpectedFunction funcParam, int intParam, String stringParam) {
if(funcParam is MyExpectedFunction) {
funcParam(intParam, stringParam);
}
}
In your case, you want to check if the function can be called with zero arguments.
typedef NullaryFunction();
main () {
var f = null;
print(f is NullaryFunction); // false
f = () {};
print(f is NullaryFunction); // true
f = (x) {};
print(f is NullaryFunction); // false
}
If you just want to know that it is some function, you can test with ... is Function. All callable objects implement Function, but it is technically possible (though often not useful) to implement Function manually without actually being callable. It does make a kind of sense for objects that mock callability through noSuchMethod.
var f = () {};
print(f is Function); // 'true'
var x = (x){};
print(x is Function); // 'true'

Is it possible to register an open generic delegate in autofac?

I want to register a generic delegate that resolves itself at runtime, but I cannot find a way to do this on generics.
Given a delegate that looks like this:
public delegate TOutput Pipe<in TInput, out TOutput>(TInput input);
And given a discretely registered delegate that look like this:
public class AnonymousPipe<TInput, TOutput>
{
public Pipe<TInput, TOutput> GetPipe(IContext context)
{...}
I want to register a function along the lines of this:
builder.RegisterGeneric(typeof(Pipe<,>)).As(ctx =>
{
var typeArray = ctx.RequestedType.GetGenericArguments();
// this can be memoized
var pipeDefinition = ctx.Resolve(typeof(AnonymousPipe<,>).MakeGenericType(typeArray));
return pipeDefinition.GetPipe(ctx);
I cannot find a way to provide an implementation of the generic as a parameter in Autofac - I may just be missing something. I know I can do this through a generic object or interface, but I want to stick with the lightness of a delegate. It makes unit testing super simple on the injection of these.
Any thoughts? I am having to do discrete registrations at the moment(one per type combination and no generics).
I can only come up with the registration source solution (the universal hammer in Autofac.)
class PipeSource : IRegistrationSource
{
public bool IsAdapterForIndividualComponents { get { return true; } }
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service,
Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var swt = service as IServiceWithType;
if (swt == null || !swt.ServiceType.IsGenericType)
yield break;
var def = swt.ServiceType.GetGenericTypeDefinition();
if (def != typeof(Pipe<,>))
yield break;
var anonPipeService = swt.ChangeType(
typeof(AnonymousPipe<,>).MakeGenericType(
swt.ServiceType.GetGenericArguments()));
var getPipeMethod = anonPipeService.ServiceType.GetMethod("GetPipe");
foreach (var anonPipeReg in registrationAccessor(anonPipeService))
{
yield return RegistrationBuilder.ForDelegate((c, p) => {
var anon = c.ResolveComponent(anonPipeReg, p);
return getPipeMethod.Invoke(anon, null); })
.As(service)
.Targeting(anonPipeReg)
.CreateRegistration();
}
}
}
Then:
builder.RegisterSource(new PipeSource());
Now, I'm certain that I can't type that code into a web page and have it actually compile and run, but it might come close :)