// Path: src/main/java/org/adridadou/ethereum/values/EthValue.java
// public static EthValue wei(final int value) {
//     return wei(BigInteger.valueOf(value));
// }
// 
// Path: src/main/java/org/adridadou/ethereum/converters/output/OutputTypeHandler.java
// public class OutputTypeHandler {
// 
//     public static final List<OutputTypeConverter> JAVA_OUTPUT_CONVERTERS = Arrays.asList(
//             new IntegerConverter(),
//             new LongConverter(),
//             new StringConverter(),
//             new BooleanConverter(),
//             new AddressConverter(),
//             new VoidConverter(),
//             new EnumConverter(),
//             new DateConverter(),
//             new BigIntegerConverter()
//     );
// 
//     private final List<OutputTypeConverter> outputConverters = new ArrayList<>();
// 
//     public OutputTypeHandler() {
//         addConverters(JAVA_OUTPUT_CONVERTERS);
//         addConverters(
//                 new ListConverter(this),
//                 new ArrayConverter(this),
//                 new CompletableFutureConverter(this),
//                 new PayableConverter(this),
//                 new SetConverter(this));
//     }
// 
//     public void addConverters(final OutputTypeConverter... converters) {
//         addConverters(Arrays.asList(converters));
//     }
// 
//     public void addConverters(final Collection<OutputTypeConverter> converters) {
//         outputConverters.addAll(converters);
//     }
// 
//     public Optional<OutputTypeConverter> getConverter(final Class<?> cls) {
//         return outputConverters.stream().filter(converter -> converter.isOfType(cls)).findFirst();
//     }
// 
//     public <T> T convertSpecificType(Object[] result, Class<T> returnType) {
//         Object[] params = new Object[result.length];
// 
//         Constructor constr = lookForNonEmptyConstructor(returnType, result);
// 
//         for (int i = 0; i < result.length; i++) {
//             params[i] = convertResult(result[i], constr.getParameterTypes()[i], constr.getGenericParameterTypes()[i]);
//         }
// 
//         try {
//             return (T) constr.newInstance(params);
//         } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
//             throw new EthereumApiException("error while converting to a specific type", e);
//         }
//     }
// 
//     private Constructor lookForNonEmptyConstructor(Class<?> returnType, Object[] result) {
//         for (Constructor constructor : returnType.getConstructors()) {
//             if (constructor.getParameterCount() > 0) {
//                 if (constructor.getParameterCount() != result.length) {
//                     throw new IllegalArgumentException("the number of arguments don't match for type " + returnType.getSimpleName() + ". Constructor has " + constructor.getParameterCount() + " and result has " + result.length);
//                 }
//                 return constructor;
//             }
//         }
//         throw new IllegalArgumentException("no constructor with arguments found! for type " + returnType.getSimpleName());
//     }
// 
//     public Object convertResult(Object result, Class<?> returnType, Type genericType) {
//         return getConverter(returnType)
//                 .map(converter -> converter.convert(result, returnType.isArray() ? returnType.getComponentType() : genericType))
//                 .orElseGet(() -> convertSpecificType(new Object[]{result}, returnType));
//     }
// }


import static org.adridadou.ethereum.values.EthValue.wei;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import org.adridadou.ethereum.converters.input.InputTypeHandler;
import org.adridadou.ethereum.converters.output.OutputTypeHandler;
import org.adridadou.ethereum.event.*;
import org.adridadou.ethereum.values.*;
import org.adridadou.exception.EthereumApiException;
import org.ethereum.core.CallTransaction;
import org.ethereum.util.ByteUtil;
import rx.Observable;
    private final EthereumEventHandler eventHandler;
    private final Map<EthAddress, Set<EthHash>> pendingTransactions = new ConcurrentHashMap<>();
    private final Map<EthAddress, BigInteger> nonces = new ConcurrentHashMap<>();
    private final InputTypeHandler inputTypeHandler;
    private final OutputTypeHandler outputTypeHandler;

    public EthereumProxy(EthereumBackend ethereum, EthereumEventHandler eventHandler, InputTypeHandler inputTypeHandler, OutputTypeHandler outputTypeHandler) {
        this.ethereum = ethereum;
        this.eventHandler = eventHandler;
        this.inputTypeHandler = inputTypeHandler;
        this.outputTypeHandler = outputTypeHandler;
        updateNonce();
        ethereum.register(eventHandler);
    }

    public SmartContract mapFromAbi(ContractAbi abi, EthAddress address, EthAccount account) {
        return new SmartContract(new CallTransaction.Contract(abi.getAbi()), account, address, this, ethereum);
    }

    public CompletableFuture<EthAddress> publish(CompiledContract contract, EthAccount account, Object... constructorArgs) {
        return createContract(contract, account, constructorArgs);
    }

    private CompletableFuture<EthAddress> createContract(CompiledContract contract, EthAccount account, Object... constructorArgs) {
        CallTransaction.Contract contractAbi = new CallTransaction.Contract(contract.getAbi().getAbi());
        CallTransaction.Function constructor = contractAbi.getConstructor();
        if (constructor == null && constructorArgs.length > 0) {
            throw new EthereumApiException("No constructor with params found");
        }
        byte[] argsEncoded = constructor == null ? new byte[0] : constructor.encodeArguments(prepareArguments(constructorArgs));
// Path: src/main/java/org/adridadou/ethereum/values/EthValue.java
// public static EthValue wei(final int value) {
//     return wei(BigInteger.valueOf(value));
// }
// 
// Path: src/main/java/org/adridadou/ethereum/converters/output/OutputTypeHandler.java
// public class OutputTypeHandler {
// 
//     public static final List<OutputTypeConverter> JAVA_OUTPUT_CONVERTERS = Arrays.asList(
//             new IntegerConverter(),
//             new LongConverter(),
//             new StringConverter(),
//             new BooleanConverter(),
//             new AddressConverter(),
//             new VoidConverter(),
//             new EnumConverter(),
//             new DateConverter(),
//             new BigIntegerConverter()
//     );
// 
//     private final List<OutputTypeConverter> outputConverters = new ArrayList<>();
// 
//     public OutputTypeHandler() {
//         addConverters(JAVA_OUTPUT_CONVERTERS);
//         addConverters(
//                 new ListConverter(this),
//                 new ArrayConverter(this),
//                 new CompletableFutureConverter(this),
//                 new PayableConverter(this),
//                 new SetConverter(this));
//     }
// 
//     public void addConverters(final OutputTypeConverter... converters) {
//         addConverters(Arrays.asList(converters));
//     }
// 
//     public void addConverters(final Collection<OutputTypeConverter> converters) {
//         outputConverters.addAll(converters);
//     }
// 
//     public Optional<OutputTypeConverter> getConverter(final Class<?> cls) {
//         return outputConverters.stream().filter(converter -> converter.isOfType(cls)).findFirst();
//     }
// 
//     public <T> T convertSpecificType(Object[] result, Class<T> returnType) {
//         Object[] params = new Object[result.length];
// 
//         Constructor constr = lookForNonEmptyConstructor(returnType, result);
// 
//         for (int i = 0; i < result.length; i++) {
//             params[i] = convertResult(result[i], constr.getParameterTypes()[i], constr.getGenericParameterTypes()[i]);
//         }
// 
//         try {
//             return (T) constr.newInstance(params);
//         } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
//             throw new EthereumApiException("error while converting to a specific type", e);
//         }
//     }
// 
//     private Constructor lookForNonEmptyConstructor(Class<?> returnType, Object[] result) {
//         for (Constructor constructor : returnType.getConstructors()) {
//             if (constructor.getParameterCount() > 0) {
//                 if (constructor.getParameterCount() != result.length) {
//                     throw new IllegalArgumentException("the number of arguments don't match for type " + returnType.getSimpleName() + ". Constructor has " + constructor.getParameterCount() + " and result has " + result.length);
//                 }
//                 return constructor;
//             }
//         }
//         throw new IllegalArgumentException("no constructor with arguments found! for type " + returnType.getSimpleName());
//     }
// 
//     public Object convertResult(Object result, Class<?> returnType, Type genericType) {
//         return getConverter(returnType)
//                 .map(converter -> converter.convert(result, returnType.isArray() ? returnType.getComponentType() : genericType))
//                 .orElseGet(() -> convertSpecificType(new Object[]{result}, returnType));
//     }
// }


// Path: src/main/java/org/adridadou/ethereum/EthereumProxy.java
import static org.adridadou.ethereum.values.EthValue.wei;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import org.adridadou.ethereum.converters.input.InputTypeHandler;
import org.adridadou.ethereum.converters.output.OutputTypeHandler;
import org.adridadou.ethereum.event.*;
import org.adridadou.ethereum.values.*;
import org.adridadou.exception.EthereumApiException;
import org.ethereum.core.CallTransaction;
import org.ethereum.util.ByteUtil;
import rx.Observable;

    private final EthereumEventHandler eventHandler;
    private final Map<EthAddress, Set<EthHash>> pendingTransactions = new ConcurrentHashMap<>();
    private final Map<EthAddress, BigInteger> nonces = new ConcurrentHashMap<>();
    private final InputTypeHandler inputTypeHandler;
    private final OutputTypeHandler outputTypeHandler;

    public EthereumProxy(EthereumBackend ethereum, EthereumEventHandler eventHandler, InputTypeHandler inputTypeHandler, OutputTypeHandler outputTypeHandler) {
        this.ethereum = ethereum;
        this.eventHandler = eventHandler;
        this.inputTypeHandler = inputTypeHandler;
        this.outputTypeHandler = outputTypeHandler;
        updateNonce();
        ethereum.register(eventHandler);
    }

    public SmartContract mapFromAbi(ContractAbi abi, EthAddress address, EthAccount account) {
        return new SmartContract(new CallTransaction.Contract(abi.getAbi()), account, address, this, ethereum);
    }

    public CompletableFuture<EthAddress> publish(CompiledContract contract, EthAccount account, Object... constructorArgs) {
        return createContract(contract, account, constructorArgs);
    }

    private CompletableFuture<EthAddress> createContract(CompiledContract contract, EthAccount account, Object... constructorArgs) {
        CallTransaction.Contract contractAbi = new CallTransaction.Contract(contract.getAbi().getAbi());
        CallTransaction.Function constructor = contractAbi.getConstructor();
        if (constructor == null && constructorArgs.length > 0) {
            throw new EthereumApiException("No constructor with params found");
        }
        byte[] argsEncoded = constructor == null ? new byte[0] : constructor.encodeArguments(prepareArguments(constructorArgs));
        return publishContract(wei(0), EthData.of(ByteUtil.merge(contract.getBinary().data, argsEncoded)), account);