Repository: blk-io/erc20-rest-service
Branch: master
Commit: b7e1933e6c21
Files: 27
Total size: 93.0 KB
Directory structure:
gitextract_nychjct6/
├── .gitignore
├── README.md
├── build.gradle
├── docker/
│ └── Dockerfile
├── generate.sh
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src/
├── main/
│ ├── java/
│ │ └── io/
│ │ └── blk/
│ │ └── erc20/
│ │ ├── Application.java
│ │ ├── ContractService.java
│ │ ├── Controller.java
│ │ ├── NodeConfiguration.java
│ │ ├── TransactionResponse.java
│ │ └── generated/
│ │ └── HumanStandardToken.java
│ └── resources/
│ ├── config/
│ │ └── application.yml
│ ├── logback.xml
│ └── solidity/
│ └── contract/
│ ├── HumanStandardToken.sol
│ ├── HumanStandardTokenFactory.sol
│ ├── StandardToken.sol
│ ├── Token.sol
│ └── build/
│ ├── HumanStandardToken.abi
│ ├── StandardToken.abi
│ └── Token.abi
└── test/
├── java/
│ └── io/
│ └── blk/
│ └── erc20/
│ └── ControllerIT.java
└── resources/
└── logback-test.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Created by .ignore support plugin (hsz.mobi)
### Java template
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Gradle template
.gradle
/build
src/main/resources/gradle.properties
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Cache of project
.gradletasknamecache
# Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
.idea
*.iml
# OS X
.DS_Store
# log files
*.log
logs
/out
================================================
FILE: README.md
================================================
# ERC-20 RESTful service
This application provides a RESTful service for creating and managing
[ERC-20 tokens](https://github.com/ethereum/EIPs/issues/20).
It has been built using [Spring Boot](https://projects.spring.io/spring-boot/), and
[web3j](https://web3j.io).
It works with both [Geth](https://github.com/ethereum/go-ethereum),
[Parity](https://github.com/paritytech/parity), and
[Quorum](https://github.com/jpmorganchase/quorum).
For Quorum, the RESTful semantics are identical, with the exception that if you wish to create
a private transaction, you populate a HTTP header name *privateFor* with a comma-separated
list of public keys
## Build
To build a runnable jar file:
```bash
./gradlew clean build
```
## Run
Using Java 1.8+:
```bash
java -jar build/libs/azure-demo-0.1.jar
```
By default the application will log to a file named erc20-web3j.log.
## Configuration
The following default properties are used in the application:
```properties
# Port for service to bind to
port=8080
# Log file path and name
logging.file=logs/erc20-rest-service.log
# Endpoint of an Ethereum or Quorum node we wish to use.
# To use IPC simply provide a file path to the socket, such as /path/to/geth.ipc
nodeEndpoint=http://localhost:22000
# The Ethereum or Quorum address we wish to use when transacting.
# Note - this address must be already unlocked in the client
fromAddress=0xed9d02e382b34818e88b88a309c7fe71e65f419d
```
You can override any of these properties by creating a file name
*application.properties* in the root directory of your application, or in
*config/application.properties* relative to your root. If you'd rather use yaml,
simply change the filename to *application.yml*.
## Usage
All available application endpoints are documented using [Swagger](http://swagger.io/).
You can view the Swagger UI at http://localhost:8080/swagger-ui.html. From here you
can perform all POST and GET requests easily to facilitate deployment of, transacting
with, and querying state of ERC-20 tokens.

## Docker
We can use [Docker](https://www.docker.com/) to easily spin up a arbritrary instance
of our service connecting to an already running Ethereum or Quorum network.
All you need to do is build the Dockerfile:
```docker
docker build -f docker/Dockerfile -t blk-io/erc20-service .
```
Then either run it with default configuration:
```docker
docker run -p 8080:8080 -v "$PWD/logs":/logs blk-io/erc20-service
```
Or with a custom configuration:
```docker
export PORT=8081
docker run -p ${PORT}:${PORT} -v "$PWD/logs":/logs \
-e ENDPOINT="http://localhost:22001" \
-e FROMADDR="0xca843569e3427144cead5e4d5999a3d0ccf92b8e" \
-e PORT="$PORT" \
blk-io/erc20-service
```
================================================
FILE: build.gradle
================================================
plugins {
id 'java'
id 'idea'
id 'eclipse'
id 'application'
id 'org.springframework.boot' version '2.1.7.RELEASE'
}
mainClassName = 'io.blk.erc20.Application'
group 'io.blk'
version '0.1.0'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web:2.1.7.RELEASE',
'io.springfox:springfox-swagger2:2.7.0',
'io.springfox:springfox-swagger-ui:2.7.0',
'org.projectlombok:lombok:1.16.16',
'org.web3j:quorum:4.+',
'org.web3j:core:4.+',
'io.reactivex.rxjava2:rxjava:2.2.0',
'com.fasterxml.jackson:jackson-bom:2.9.4',
'org.apache.httpcomponents:httpclient:4.5.3'
testCompile 'junit:junit:4.12',
'org.springframework.boot:spring-boot-starter-test:2.1.7.RELEASE',
implementation("org.web3j:contracts:4.2.0") { exclude group: 'org.web3j' }
}
run {
/* Can pass all the properties: */
systemProperties System.getProperties()
/* Or just each by name: */
systemProperty "nodeEndpoint", System.getProperty("nodeEndpoint")
systemProperty "fromAddress", System.getProperty("fromAddress")
/* Need to split the space-delimited value in the exec.args */
args System.getProperty("exec.args", "").split()
}
================================================
FILE: docker/Dockerfile
================================================
FROM frolvlad/alpine-java
RUN apk update && apk upgrade && \
apk add --no-cache bash git openssh
RUN mkdir -p /app/erc20-rest-service
RUN git clone https://github.com/blk-io/erc20-rest-service.git
# We exclude running tests as we need a Ethereum/Quorum network to be running
RUN cd erc20-rest-service \
&& ./gradlew build -x test \
&& cp build/libs/erc20-rest-service-0.1.0.jar /app/erc20-rest-service
ENV PORT=8080
ENV ENDPOINT="http://localhost:22000"
ENV FROMADDR="0xed9d02e382b34818e88b88a309c7fe71e65f419d"
ENTRYPOINT ["/usr/bin/java"]
# We can define an environment variable to interpolate these values, as Docker does not support
# this functionality, hence we have to do it in the command.
CMD ["-jar", "/app/erc20-rest-service/erc20-rest-service-0.1.0.jar", \
"--spring.application.json={\"nodeEndpoint\":\"${ENDPOINT}\",\"fromAddress\":\"${FROMADDR}\"}"]
================================================
FILE: generate.sh
================================================
#!/usr/bin/env bash
cd src/main/resources/solidity/contract/ && \
solc --bin --abi --optimize --overwrite HumanStandardToken.sol -o build/ && \
web3j solidity generate \
--binFile build/HumanStandardToken.bin \
--abiFile build/HumanStandardToken.abi \
-p io.blk.erc20.generated \
-o ../../../java/
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Mon Nov 18 15:02:41 GMT 2019
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
rootProject.name = 'erc20-rest-service'
================================================
FILE: src/main/java/io/blk/erc20/Application.java
================================================
package io.blk.erc20;
import com.google.common.base.Predicates;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.web3j.protocol.Web3jService;
import org.web3j.protocol.http.HttpService;
import org.web3j.protocol.ipc.UnixIpcService;
import org.web3j.protocol.ipc.WindowsIpcService;
import org.web3j.quorum.Quorum;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Our main application class.
*/
@EnableSwagger2
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
@Autowired
NodeConfiguration nodeConfiguration;
@Bean
Quorum quorum() {
String nodeEndpoint = nodeConfiguration.getNodeEndpoint();
Web3jService web3jService;
if (nodeEndpoint == null || nodeEndpoint.equals("")) {
web3jService = new HttpService();
} else if (nodeEndpoint.startsWith("http")) {
web3jService = new HttpService(nodeEndpoint);
} else if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
web3jService = new WindowsIpcService(nodeEndpoint);
} else {
web3jService = new UnixIpcService(nodeEndpoint);
}
return Quorum.build(web3jService);
}
@Bean
public Docket lenderApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
// see https://github.com/springfox/springfox/issues/631
.apis(Predicates.not(
RequestHandlerSelectors.basePackage("org.springframework.boot")))
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("ERC-20 API")
.description("ERC-20 token standard RESTful service")
.build();
}
}
================================================
FILE: src/main/java/io/blk/erc20/ContractService.java
================================================
package io.blk.erc20;
import java.math.BigInteger;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import io.blk.erc20.generated.HumanStandardToken;
import io.reactivex.annotations.Nullable;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.quorum.Quorum;
import org.web3j.quorum.tx.ClientTransactionManager;
import org.web3j.tx.TransactionManager;
import static org.web3j.tx.Contract.GAS_LIMIT;
import static org.web3j.tx.ManagedTransaction.GAS_PRICE;
/**
* Our smart contract service.
*/
@Service
public class ContractService {
private final Quorum quorum;
private final NodeConfiguration nodeConfiguration;
@Autowired
public ContractService(Quorum quorum, NodeConfiguration nodeConfiguration) {
this.quorum = quorum;
this.nodeConfiguration = nodeConfiguration;
}
public NodeConfiguration getConfig() {
return nodeConfiguration;
}
public String deploy(
List<String> privateFor, BigInteger initialAmount, String tokenName, BigInteger decimalUnits,
String tokenSymbol) throws Exception {
try {
TransactionManager transactionManager = new ClientTransactionManager(
quorum, nodeConfiguration.getFromAddress(), privateFor);
HumanStandardToken humanStandardToken = HumanStandardToken.deploy(
quorum, transactionManager, GAS_PRICE, GAS_LIMIT,
initialAmount, tokenName, decimalUnits,
tokenSymbol).send();
return humanStandardToken.getContractAddress();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public String name(String contractAddress) throws Exception {
HumanStandardToken humanStandardToken = load(contractAddress);
try {
return humanStandardToken.name().send();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public TransactionResponse<ApprovalEventResponse> approve(
List<String> privateFor, String contractAddress, String spender, BigInteger value) throws Exception {
HumanStandardToken humanStandardToken = load(contractAddress, privateFor);
try {
TransactionReceipt transactionReceipt = humanStandardToken
.approve(spender, value).send();
return processApprovalEventResponse(humanStandardToken, transactionReceipt);
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public String totalSupply(String contractAddress) throws Exception {
HumanStandardToken humanStandardToken = load(contractAddress);
try {
return humanStandardToken.totalSupply().send().toString();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public TransactionResponse<TransferEventResponse> transferFrom(
List<String> privateFor, String contractAddress, String from, String to, BigInteger value) throws Exception {
HumanStandardToken humanStandardToken = load(contractAddress, privateFor);
try {
TransactionReceipt transactionReceipt = humanStandardToken
.transferFrom(from, to, value).send();
return processTransferEventsResponse(humanStandardToken, transactionReceipt);
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public String decimals(String contractAddress) throws Exception {
HumanStandardToken humanStandardToken = load(contractAddress);
try {
return humanStandardToken.decimals().send().toString();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public String version(String contractAddress) throws Exception {
HumanStandardToken humanStandardToken = load(contractAddress);
try {
return humanStandardToken.version().send();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public String balanceOf(String contractAddress, String ownerAddress) throws Exception {
HumanStandardToken humanStandardToken = load(contractAddress);
try {
return humanStandardToken.balanceOf(ownerAddress).send().toString();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public String symbol(String contractAddress) throws Exception {
HumanStandardToken humanStandardToken = load(contractAddress);
try {
return humanStandardToken.symbol().send();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public TransactionResponse<TransferEventResponse> transfer(
List<String> privateFor, String contractAddress, String to, BigInteger value) throws Exception {
HumanStandardToken humanStandardToken = load(contractAddress, privateFor);
try {
TransactionReceipt transactionReceipt = humanStandardToken
.transfer(to, value).send();
return processTransferEventsResponse(humanStandardToken, transactionReceipt);
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public TransactionResponse<ApprovalEventResponse> approveAndCall(
@Nullable List<String> privateFor, String contractAddress, String spender, BigInteger value,
String extraData) throws Exception {
HumanStandardToken humanStandardToken = load(contractAddress, privateFor);
try {
TransactionReceipt transactionReceipt = humanStandardToken
.approveAndCall(
spender, value,
extraData.getBytes())
.send();
return processApprovalEventResponse(humanStandardToken, transactionReceipt);
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
public String allowance(String contractAddress, String ownerAddress, String spenderAddress) throws Exception {
HumanStandardToken humanStandardToken = load(contractAddress);
try {
return humanStandardToken.allowance(
ownerAddress, spenderAddress)
.send().toString();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
private HumanStandardToken load(String contractAddress, List<String> privateFor) {
TransactionManager transactionManager = new ClientTransactionManager(
quorum, nodeConfiguration.getFromAddress(), privateFor);
return HumanStandardToken.load(
contractAddress, quorum, transactionManager, GAS_PRICE, GAS_LIMIT);
}
private HumanStandardToken load(String contractAddress) {
TransactionManager transactionManager = new ClientTransactionManager(
quorum, nodeConfiguration.getFromAddress(), Collections.emptyList());
return HumanStandardToken.load(
contractAddress, quorum, transactionManager, GAS_PRICE, GAS_LIMIT);
}
private TransactionResponse<ApprovalEventResponse>
processApprovalEventResponse(
HumanStandardToken humanStandardToken,
TransactionReceipt transactionReceipt) {
return processEventResponse(
humanStandardToken.getApprovalEvents(transactionReceipt),
transactionReceipt,
ApprovalEventResponse::new);
}
private TransactionResponse<TransferEventResponse>
processTransferEventsResponse(
HumanStandardToken humanStandardToken,
TransactionReceipt transactionReceipt) {
return processEventResponse(
humanStandardToken.getTransferEvents(transactionReceipt),
transactionReceipt,
TransferEventResponse::new);
}
private <T, R> TransactionResponse<R> processEventResponse(
List<T> eventResponses, TransactionReceipt transactionReceipt, Function<T, R> map) {
if (!eventResponses.isEmpty()) {
return new TransactionResponse<>(
transactionReceipt.getTransactionHash(),
map.apply(eventResponses.get(0)));
} else {
return new TransactionResponse<>(
transactionReceipt.getTransactionHash());
}
}
@Getter
@Setter
public static class TransferEventResponse {
private String from;
private String to;
private long value;
public TransferEventResponse() { }
public TransferEventResponse(
HumanStandardToken.TransferEventResponse transferEventResponse) {
this.from = transferEventResponse._from;
this.to = transferEventResponse._to;
this.value = transferEventResponse._value.longValueExact();
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
@Getter
@Setter
public static class ApprovalEventResponse {
private String owner;
private String spender;
private long value;
public ApprovalEventResponse() { }
public ApprovalEventResponse(
HumanStandardToken.ApprovalEventResponse approvalEventResponse) {
this.owner = approvalEventResponse._owner;
this.spender = approvalEventResponse._spender;
this.value = approvalEventResponse._value.longValueExact();
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getSpender() {
return spender;
}
public void setSpender(String spender) {
this.spender = spender;
}
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
}
================================================
FILE: src/main/java/io/blk/erc20/Controller.java
================================================
package io.blk.erc20;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import io.reactivex.annotations.Nullable;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* Controller for our ERC-20 contract API.
*/
@Api("ERC-20 token standard API")
@RestController
public class Controller {
private final ContractService ContractService;
@Autowired
public Controller(ContractService ContractService) {
this.ContractService = ContractService;
}
@ApiOperation("Application configuration")
@RequestMapping(value = "/config", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
NodeConfiguration config() {
return ContractService.getConfig();
}
@ApiOperation(
value = "Deploy new ERC-20 token",
notes = "Returns hex encoded contract address")
@ApiImplicitParam(name = "privateFor",
value = "Comma separated list of public keys of enclave nodes that transaction is "
+ "private for",
paramType = "header",
dataType = "string")
@RequestMapping(value = "/deploy", method = RequestMethod.POST)
String deploy(
HttpServletRequest request,
@RequestBody ContractSpecification contractSpecification) throws Exception {
return ContractService.deploy(
extractPrivateFor(request),
contractSpecification.getInitialAmount(),
contractSpecification.getTokenName(),
contractSpecification.getDecimalUnits(),
contractSpecification.getTokenSymbol());
}
@ApiOperation("Get token name")
@RequestMapping(value = "/{contractAddress}/name", method = RequestMethod.GET)
String name(@PathVariable String contractAddress) throws Exception {
return ContractService.name(contractAddress);
}
@ApiOperation(
value = "Approve transfers by a specific address up to the provided total quantity",
notes = "Returns hex encoded transaction hash, and Approval event if called")
@ApiImplicitParam(name = "privateFor",
value = "Comma separated list of public keys of enclave nodes that transaction is "
+ "private for",
paramType = "header",
dataType = "string")
@RequestMapping(value = "/{contractAddress}/approve", method = RequestMethod.POST)
TransactionResponse<ContractService.ApprovalEventResponse> approve(
HttpServletRequest request,
@PathVariable String contractAddress,
@RequestBody ApproveRequest approveRequest) throws Exception {
return ContractService.approve(
extractPrivateFor(request),
contractAddress,
approveRequest.getSpender(),
approveRequest.getValue());
}
@ApiOperation("Get total supply of tokens")
@RequestMapping(value = "/{contractAddress}/totalSupply", method = RequestMethod.GET)
String totalSupply(@PathVariable String contractAddress) throws Exception {
return ContractService.totalSupply(contractAddress);
}
@ApiOperation(
value = "Transfer tokens between addresses (must already be approved)",
notes = "Returns hex encoded transaction hash, and Transfer event if called")
@ApiImplicitParam(name = "privateFor",
value = "Comma separated list of public keys of enclave nodes that transaction is "
+ "private for",
paramType = "header",
dataType = "string")
@RequestMapping(value = "/{contractAddress}/transferFrom", method = RequestMethod.POST)
TransactionResponse<ContractService.TransferEventResponse> transferFrom(
HttpServletRequest request,
@PathVariable String contractAddress,
@RequestBody TransferFromRequest transferFromRequest) throws Exception {
return ContractService.transferFrom(
extractPrivateFor(request),
contractAddress,
transferFromRequest.getFrom(),
transferFromRequest.getTo(),
transferFromRequest.getValue());
}
@ApiOperation("Get decimal precision of tokens")
@RequestMapping(value = "/{contractAddress}/decimals", method = RequestMethod.GET)
String decimals(@PathVariable String contractAddress) throws Exception {
return ContractService.decimals(contractAddress);
}
@ApiOperation("Get contract version")
@RequestMapping(value = "/{contractAddress}/version", method = RequestMethod.GET)
String version(@PathVariable String contractAddress) throws Exception {
return ContractService.version(contractAddress);
}
@ApiOperation("Get token balance for address")
@RequestMapping(
value = "/{contractAddress}/balanceOf/{ownerAddress}", method = RequestMethod.GET)
String balanceOf(
@PathVariable String contractAddress,
@PathVariable String ownerAddress) throws Exception {
return ContractService.balanceOf(contractAddress, ownerAddress);
}
@ApiOperation("Get token symbol")
@RequestMapping(value = "/{contractAddress}/symbol", method = RequestMethod.GET)
String symbol(@PathVariable String contractAddress) throws Exception {
return ContractService.symbol(contractAddress);
}
@ApiOperation(
value = "Transfer tokens you own to another address",
notes = "Returns hex encoded transaction hash, and Transfer event if called")
@ApiImplicitParam(name = "privateFor",
value = "Comma separated list of public keys of enclave nodes that transaction is "
+ "private for",
paramType = "header",
dataType = "string")
@RequestMapping(value = "/{contractAddress}/transfer", method = RequestMethod.POST)
TransactionResponse<ContractService.TransferEventResponse> transfer(
HttpServletRequest request,
@PathVariable String contractAddress,
@RequestBody TransferRequest transferRequest) throws Exception {
return ContractService.transfer(
extractPrivateFor(request),
contractAddress,
transferRequest.getTo(),
transferRequest.getValue());
}
@ApiOperation(
value = "Approve transfers by a specific contract address up to the provided total "
+ "quantity, and notify that contract address of the approval",
notes = "Returns hex encoded transaction hash, and Approval event if called")
@ApiImplicitParam(name = "privateFor",
value = "Comma separated list of public keys of enclave nodes that transaction is "
+ "private for",
paramType = "header",
dataType = "string")
@RequestMapping(value = "/{contractAddress}/approveAndCall", method = RequestMethod.POST)
TransactionResponse<ContractService.ApprovalEventResponse> approveAndCall(
HttpServletRequest request,
@PathVariable String contractAddress,
@RequestBody ApproveAndCallRequest approveAndCallRequest) throws Exception {
return ContractService.approveAndCall(
extractPrivateFor(request),
contractAddress,
approveAndCallRequest.getSpender(),
approveAndCallRequest.getValue(),
approveAndCallRequest.getExtraData());
}
@ApiOperation("Get quantity of tokens you can transfer on another token holder's behalf")
@RequestMapping(value = "/{contractAddress}/allowance", method = RequestMethod.GET)
String allowance(
@PathVariable String contractAddress,
@RequestParam String ownerAddress,
@RequestParam String spenderAddress) throws Exception {
return ContractService.allowance(
contractAddress, ownerAddress, spenderAddress);
}
private static @Nullable List<String> extractPrivateFor(HttpServletRequest request) {
String privateFor = request.getHeader("privateFor");
if (privateFor == null) {
return null;
} else {
return Arrays.asList(privateFor.split(","));
}
}
@Data
static class ContractSpecification {
private BigInteger initialAmount;
private String tokenName;
private BigInteger decimalUnits;
private String tokenSymbol;
ContractSpecification() {
}
ContractSpecification(BigInteger initialAmount, String tokenName, BigInteger decimalUnits, String tokenSymbol) {
this.initialAmount = initialAmount;
this.tokenName = tokenName;
this.decimalUnits = decimalUnits;
this.tokenSymbol = tokenSymbol;
}
public BigInteger getDecimalUnits() {
return decimalUnits;
}
public BigInteger getInitialAmount() {
return initialAmount;
}
public String getTokenName() {
return tokenName;
}
public String getTokenSymbol() {
return tokenSymbol;
}
}
@Data
static class ApproveRequest {
private String spender;
private BigInteger value;
ApproveRequest() {}
ApproveRequest(String spender, BigInteger value) {
this.spender = spender;
this.value = value;
}
public String getSpender() {
return spender;
}
public BigInteger getValue() {
return value;
}
}
@Data
static class TransferFromRequest {
private String from;
private String to;
private BigInteger value;
TransferFromRequest() {}
TransferFromRequest(String from, String to, BigInteger value) {
this.from = from;
this.to = to;
this.value = value;
}
public String getFrom() {
return from;
}
BigInteger getValue() {
return value;
}
public String getTo() {
return to;
}
}
@Data
static class TransferRequest {
private String to;
private BigInteger value;
TransferRequest(String to, BigInteger value) {
this.to = to;
this.value = value;
}
TransferRequest() {}
public String getTo() {
return to;
}
public BigInteger getValue() {
return value;
}
}
@Data
static class ApproveAndCallRequest {
private String spender;
private BigInteger value;
private String extraData;
ApproveAndCallRequest() {}
ApproveAndCallRequest(String spender, BigInteger value, String extraData) {
this.spender = spender;
this.value = value;
this.extraData = extraData;
}
String getSpender() {
return spender;
}
BigInteger getValue() {
return value;
}
String getExtraData() {
return extraData;
}
}
@Data
static class AllowanceRequest {
private String ownerAddress;
private String spenderAddress;
AllowanceRequest() {}
AllowanceRequest(String ownerAddress, String spenderAddress) {
this.ownerAddress = ownerAddress;
this.spenderAddress = spenderAddress;
}
public String getOwnerAddress() {
return ownerAddress;
}
public void setOwnerAddress(String ownerAddress) {
this.ownerAddress = ownerAddress;
}
public String getSpenderAddress() {
return spenderAddress;
}
public void setSpenderAddress(String spenderAddress) {
this.spenderAddress = spenderAddress;
}
}
}
================================================
FILE: src/main/java/io/blk/erc20/NodeConfiguration.java
================================================
package io.blk.erc20;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Node configuration bean.
*/
@Data
@ConfigurationProperties("io.blk.erc20")
@Component
public class NodeConfiguration {
private String nodeEndpoint = System.getProperty("nodeEndpoint");
private String fromAddress = System.getProperty("fromAddress");
public String getNodeEndpoint() {
return nodeEndpoint;
}
public void setNodeEndpoint(String nodeEndpoint) {
this.nodeEndpoint = nodeEndpoint;
}
public String getFromAddress() {
return fromAddress;
}
public void setFromAddress(String fromAddress) {
this.fromAddress = fromAddress;
}
}
================================================
FILE: src/main/java/io/blk/erc20/TransactionResponse.java
================================================
package io.blk.erc20;
import lombok.Getter;
import lombok.Setter;
/**
* TransactionResponse wrapper.
*/
@Getter
@Setter
public class TransactionResponse<T> {
private String transactionHash;
private T event;
TransactionResponse() { }
public TransactionResponse(String transactionHash) {
this(transactionHash, null);
}
public TransactionResponse(String transactionHash, T event) {
this.transactionHash = transactionHash;
this.event = event;
}
public String getTransactionHash() {
return transactionHash;
}
public void setTransactionHash(String transactionHash) {
this.transactionHash = transactionHash;
}
public T getEvent() {
return event;
}
public void setEvent(T event) {
this.event = event;
}
}
================================================
FILE: src/main/java/io/blk/erc20/generated/HumanStandardToken.java
================================================
package io.blk.erc20.generated;
import io.reactivex.Flowable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.web3j.abi.EventEncoder;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Event;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.abi.datatypes.generated.Uint8;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.protocol.core.methods.request.EthFilter;
import org.web3j.protocol.core.methods.response.Log;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.tx.Contract;
import org.web3j.tx.TransactionManager;
import org.web3j.tx.gas.ContractGasProvider;
/**
* <p>Auto generated code.
* <p><strong>Do not modify!</strong>
* <p>Please use the <a href="https://docs.web3j.io/command_line.html">web3j command line tools</a>,
* or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the
* <a href="https://github.com/web3j/web3j/tree/master/codegen">codegen module</a> to update.
*
* <p>Generated with web3j version 4.3.0.
*/
public class HumanStandardToken extends Contract {
private static final String BINARY = "60c0604052600460808190527f48302e310000000000000000000000000000000000000000000000000000000060a090815261003e916006919061016b565b5034801561004b57600080fd5b50604051610a4f380380610a4f8339810180604052608081101561006e57600080fd5b81516020830180519193928301929164010000000081111561008f57600080fd5b820160208101848111156100a257600080fd5b81516401000000008111828201871017156100bc57600080fd5b505060208201516040909201805191949293916401000000008111156100e157600080fd5b820160208101848111156100f457600080fd5b815164010000000081118282018710171561010e57600080fd5b5050336000908152600160209081526040822089905590889055865191945061013e93506003925086019061016b565b506004805460ff191660ff8416179055805161016190600590602084019061016b565b5050505050610206565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106101ac57805160ff19168380011785556101d9565b828001600101855582156101d9579182015b828111156101d95782518255916020019190600101906101be565b506101e59291506101e9565b5090565b61020391905b808211156101e557600081556001016101ef565b90565b61083a806102156000396000f3fe608060405234801561001057600080fd5b50600436106100c6576000357c01000000000000000000000000000000000000000000000000000000009004806354fd4d501161008e57806354fd4d50146101f657806370a08231146101fe57806395d89b4114610224578063a9059cbb1461022c578063cae9ca5114610258578063dd62ed3e146102dd576100c6565b806306fdde03146100cb578063095ea7b31461014857806318160ddd1461018857806323b872dd146101a2578063313ce567146101d8575b600080fd5b6100d361030b565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561010d5781810151838201526020016100f5565b50505050905090810190601f16801561013a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101746004803603604081101561015e57600080fd5b50600160a060020a038135169060200135610399565b604080519115158252519081900360200190f35b610190610400565b60408051918252519081900360200190f35b610174600480360360608110156101b857600080fd5b50600160a060020a03813581169160208101359091169060400135610406565b6101e06104f3565b6040805160ff9092168252519081900360200190f35b6100d36104fc565b6101906004803603602081101561021457600080fd5b5035600160a060020a0316610557565b6100d3610572565b6101746004803603604081101561024257600080fd5b50600160a060020a0381351690602001356105cd565b6101746004803603606081101561026e57600080fd5b600160a060020a038235169160208101359181019060608101604082013564010000000081111561029e57600080fd5b8201836020820111156102b057600080fd5b803590602001918460018302840111640100000000831117156102d257600080fd5b509092509050610666565b610190600480360360408110156102f357600080fd5b50600160a060020a03813581169160200135166107b5565b6003805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156103915780601f1061036657610100808354040283529160200191610391565b820191906000526020600020905b81548152906001019060200180831161037457829003601f168201915b505050505081565b336000818152600260209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60005481565b600160a060020a03831660009081526001602052604081205482118015906104515750600160a060020a03841660009081526002602090815260408083203384529091529020548211155b801561045d5750600082115b156104e857600160a060020a03808416600081815260016020908152604080832080548801905593881680835284832080548890039055600282528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016104ec565b5060005b9392505050565b60045460ff1681565b6006805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156103915780601f1061036657610100808354040283529160200191610391565b600160a060020a031660009081526001602052604090205490565b6005805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156103915780601f1061036657610100808354040283529160200191610391565b3360009081526001602052604081205482118015906105ec5750600082115b1561065e5733600081815260016020908152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016103fa565b5060006103fa565b336000818152600260209081526040808320600160a060020a038916808552908352818420889055815188815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a3600085600160a060020a031660405160200180806020018281038252602e8152602001806107e1602e91396040019150506040516020818303038152906040526040518082805190602001908083835b602083106107325780518252601f199092019160209182019101610713565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610794576040519150601f19603f3d011682016040523d82523d6000602084013e610799565b606091505b505090508015156107a957600080fd5b50600195945050505050565b600160a060020a0391821660009081526002602090815260408083209390941682529190915220549056fe72656365697665417070726f76616c28616464726573732c75696e743235362c616464726573732c627974657329a165627a7a72305820d15a070a95051e159632a5f42da17cdc0b4e940c8c7574a86370e2f434405abd0029";
public static final String FUNC_NAME = "name";
public static final String FUNC_APPROVE = "approve";
public static final String FUNC_TOTALSUPPLY = "totalSupply";
public static final String FUNC_TRANSFERFROM = "transferFrom";
public static final String FUNC_DECIMALS = "decimals";
public static final String FUNC_VERSION = "version";
public static final String FUNC_BALANCEOF = "balanceOf";
public static final String FUNC_SYMBOL = "symbol";
public static final String FUNC_TRANSFER = "transfer";
public static final String FUNC_APPROVEANDCALL = "approveAndCall";
public static final String FUNC_ALLOWANCE = "allowance";
public static final Event TRANSFER_EVENT = new Event("Transfer",
Arrays.<TypeReference<?>>asList(new TypeReference<Address>(true) {}, new TypeReference<Address>(true) {}, new TypeReference<Uint256>() {}));
;
public static final Event APPROVAL_EVENT = new Event("Approval",
Arrays.<TypeReference<?>>asList(new TypeReference<Address>(true) {}, new TypeReference<Address>(true) {}, new TypeReference<Uint256>() {}));
;
@Deprecated
protected HumanStandardToken(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
}
protected HumanStandardToken(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
super(BINARY, contractAddress, web3j, credentials, contractGasProvider);
}
@Deprecated
protected HumanStandardToken(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}
protected HumanStandardToken(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider);
}
public RemoteCall<String> name() {
final Function function = new Function(FUNC_NAME,
Arrays.<Type>asList(),
Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {}));
return executeRemoteCallSingleValueReturn(function, String.class);
}
public RemoteCall<TransactionReceipt> approve(String _spender, BigInteger _value) {
final Function function = new Function(
FUNC_APPROVE,
Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(_spender),
new org.web3j.abi.datatypes.generated.Uint256(_value)),
Collections.<TypeReference<?>>emptyList());
return executeRemoteCallTransaction(function);
}
public RemoteCall<BigInteger> totalSupply() {
final Function function = new Function(FUNC_TOTALSUPPLY,
Arrays.<Type>asList(),
Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {}));
return executeRemoteCallSingleValueReturn(function, BigInteger.class);
}
public RemoteCall<TransactionReceipt> transferFrom(String _from, String _to, BigInteger _value) {
final Function function = new Function(
FUNC_TRANSFERFROM,
Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(_from),
new org.web3j.abi.datatypes.Address(_to),
new org.web3j.abi.datatypes.generated.Uint256(_value)),
Collections.<TypeReference<?>>emptyList());
return executeRemoteCallTransaction(function);
}
public RemoteCall<BigInteger> decimals() {
final Function function = new Function(FUNC_DECIMALS,
Arrays.<Type>asList(),
Arrays.<TypeReference<?>>asList(new TypeReference<Uint8>() {}));
return executeRemoteCallSingleValueReturn(function, BigInteger.class);
}
public RemoteCall<String> version() {
final Function function = new Function(FUNC_VERSION,
Arrays.<Type>asList(),
Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {}));
return executeRemoteCallSingleValueReturn(function, String.class);
}
public RemoteCall<BigInteger> balanceOf(String _owner) {
final Function function = new Function(FUNC_BALANCEOF,
Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(_owner)),
Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {}));
return executeRemoteCallSingleValueReturn(function, BigInteger.class);
}
public RemoteCall<String> symbol() {
final Function function = new Function(FUNC_SYMBOL,
Arrays.<Type>asList(),
Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {}));
return executeRemoteCallSingleValueReturn(function, String.class);
}
public RemoteCall<TransactionReceipt> transfer(String _to, BigInteger _value) {
final Function function = new Function(
FUNC_TRANSFER,
Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(_to),
new org.web3j.abi.datatypes.generated.Uint256(_value)),
Collections.<TypeReference<?>>emptyList());
return executeRemoteCallTransaction(function);
}
public RemoteCall<TransactionReceipt> approveAndCall(String _spender, BigInteger _value, byte[] _extraData) {
final Function function = new Function(
FUNC_APPROVEANDCALL,
Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(_spender),
new org.web3j.abi.datatypes.generated.Uint256(_value),
new org.web3j.abi.datatypes.DynamicBytes(_extraData)),
Collections.<TypeReference<?>>emptyList());
return executeRemoteCallTransaction(function);
}
public RemoteCall<BigInteger> allowance(String _owner, String _spender) {
final Function function = new Function(FUNC_ALLOWANCE,
Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(_owner),
new org.web3j.abi.datatypes.Address(_spender)),
Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {}));
return executeRemoteCallSingleValueReturn(function, BigInteger.class);
}
public List<TransferEventResponse> getTransferEvents(TransactionReceipt transactionReceipt) {
List<Contract.EventValuesWithLog> valueList = extractEventParametersWithLog(TRANSFER_EVENT, transactionReceipt);
ArrayList<TransferEventResponse> responses = new ArrayList<TransferEventResponse>(valueList.size());
for (Contract.EventValuesWithLog eventValues : valueList) {
TransferEventResponse typedResponse = new TransferEventResponse();
typedResponse.log = eventValues.getLog();
typedResponse._from = (String) eventValues.getIndexedValues().get(0).getValue();
typedResponse._to = (String) eventValues.getIndexedValues().get(1).getValue();
typedResponse._value = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue();
responses.add(typedResponse);
}
return responses;
}
public Flowable<TransferEventResponse> transferEventFlowable(EthFilter filter) {
return web3j.ethLogFlowable(filter).map(new io.reactivex.functions.Function<Log, TransferEventResponse>() {
@Override
public TransferEventResponse apply(Log log) {
Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(TRANSFER_EVENT, log);
TransferEventResponse typedResponse = new TransferEventResponse();
typedResponse.log = log;
typedResponse._from = (String) eventValues.getIndexedValues().get(0).getValue();
typedResponse._to = (String) eventValues.getIndexedValues().get(1).getValue();
typedResponse._value = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue();
return typedResponse;
}
});
}
public Flowable<TransferEventResponse> transferEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) {
EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress());
filter.addSingleTopic(EventEncoder.encode(TRANSFER_EVENT));
return transferEventFlowable(filter);
}
public List<ApprovalEventResponse> getApprovalEvents(TransactionReceipt transactionReceipt) {
List<Contract.EventValuesWithLog> valueList = extractEventParametersWithLog(APPROVAL_EVENT, transactionReceipt);
ArrayList<ApprovalEventResponse> responses = new ArrayList<ApprovalEventResponse>(valueList.size());
for (Contract.EventValuesWithLog eventValues : valueList) {
ApprovalEventResponse typedResponse = new ApprovalEventResponse();
typedResponse.log = eventValues.getLog();
typedResponse._owner = (String) eventValues.getIndexedValues().get(0).getValue();
typedResponse._spender = (String) eventValues.getIndexedValues().get(1).getValue();
typedResponse._value = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue();
responses.add(typedResponse);
}
return responses;
}
public Flowable<ApprovalEventResponse> approvalEventFlowable(EthFilter filter) {
return web3j.ethLogFlowable(filter).map(new io.reactivex.functions.Function<Log, ApprovalEventResponse>() {
@Override
public ApprovalEventResponse apply(Log log) {
Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(APPROVAL_EVENT, log);
ApprovalEventResponse typedResponse = new ApprovalEventResponse();
typedResponse.log = log;
typedResponse._owner = (String) eventValues.getIndexedValues().get(0).getValue();
typedResponse._spender = (String) eventValues.getIndexedValues().get(1).getValue();
typedResponse._value = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue();
return typedResponse;
}
});
}
public Flowable<ApprovalEventResponse> approvalEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) {
EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress());
filter.addSingleTopic(EventEncoder.encode(APPROVAL_EVENT));
return approvalEventFlowable(filter);
}
@Deprecated
public static HumanStandardToken load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
return new HumanStandardToken(contractAddress, web3j, credentials, gasPrice, gasLimit);
}
@Deprecated
public static HumanStandardToken load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
return new HumanStandardToken(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}
public static HumanStandardToken load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
return new HumanStandardToken(contractAddress, web3j, credentials, contractGasProvider);
}
public static HumanStandardToken load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
return new HumanStandardToken(contractAddress, web3j, transactionManager, contractGasProvider);
}
public static RemoteCall<HumanStandardToken> deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider, BigInteger _initialAmount, String _tokenName, BigInteger _decimalUnits, String _tokenSymbol) {
String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Uint256(_initialAmount),
new org.web3j.abi.datatypes.Utf8String(_tokenName),
new org.web3j.abi.datatypes.generated.Uint8(_decimalUnits),
new org.web3j.abi.datatypes.Utf8String(_tokenSymbol)));
return deployRemoteCall(HumanStandardToken.class, web3j, credentials, contractGasProvider, BINARY, encodedConstructor);
}
public static RemoteCall<HumanStandardToken> deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider, BigInteger _initialAmount, String _tokenName, BigInteger _decimalUnits, String _tokenSymbol) {
String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Uint256(_initialAmount),
new org.web3j.abi.datatypes.Utf8String(_tokenName),
new org.web3j.abi.datatypes.generated.Uint8(_decimalUnits),
new org.web3j.abi.datatypes.Utf8String(_tokenSymbol)));
return deployRemoteCall(HumanStandardToken.class, web3j, transactionManager, contractGasProvider, BINARY, encodedConstructor);
}
@Deprecated
public static RemoteCall<HumanStandardToken> deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, BigInteger _initialAmount, String _tokenName, BigInteger _decimalUnits, String _tokenSymbol) {
String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Uint256(_initialAmount),
new org.web3j.abi.datatypes.Utf8String(_tokenName),
new org.web3j.abi.datatypes.generated.Uint8(_decimalUnits),
new org.web3j.abi.datatypes.Utf8String(_tokenSymbol)));
return deployRemoteCall(HumanStandardToken.class, web3j, credentials, gasPrice, gasLimit, BINARY, encodedConstructor);
}
@Deprecated
public static RemoteCall<HumanStandardToken> deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, BigInteger _initialAmount, String _tokenName, BigInteger _decimalUnits, String _tokenSymbol) {
String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Uint256(_initialAmount),
new org.web3j.abi.datatypes.Utf8String(_tokenName),
new org.web3j.abi.datatypes.generated.Uint8(_decimalUnits),
new org.web3j.abi.datatypes.Utf8String(_tokenSymbol)));
return deployRemoteCall(HumanStandardToken.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, encodedConstructor);
}
public static class TransferEventResponse {
public Log log;
public String _from;
public String _to;
public BigInteger _value;
}
public static class ApprovalEventResponse {
public Log log;
public String _owner;
public String _spender;
public BigInteger _value;
}
}
================================================
FILE: src/main/resources/config/application.yml
================================================
# Port to run on
server:
port: ${port:8081}
# Our log file path and name
logging:
file: logs/erc20-rest-service.log
# Endpoint of an Ethereum or Quorum node we wish to use.
# To use IPC simply provide a file path to the socket, such as /path/to/geth.ipc
nodeEndpoint: http://localhost:22000
# The Ethereum or Quorum address we wish to use when transacting.
# Note - this address must be already unlocked in the client
fromAddress: "0xed9d02e382b34818e88b88a309c7fe71e65f419d"
================================================
FILE: src/main/resources/logback.xml
================================================
<!--<?xml version="1.0" encoding="UTF-8"?>-->
<!--<configuration>-->
<!-- <!– Prevent Spring trying to log to /tmp/spring.log on Linux hosts. This needs to be before-->
<!-- the defaults include –>-->
<!-- <property name="LOG_TEMP" value="./logs" />-->
<!-- <include resource="org/springframework/boot/logging/logback/defaults.xml"/>-->
<!-- <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>-->
<!-- <!– Uncomment here and below to allow console logging too –>-->
<!-- <!–<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>–>-->
<!-- <appender name="ROLLING-FILE"-->
<!-- class="ch.qos.logback.core.rolling.RollingFileAppender">-->
<!-- <encoder>-->
<!-- <pattern>${FILE_LOG_PATTERN}</pattern>-->
<!-- </encoder>-->
<!-- <file>${LOG_FILE}</file>-->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- <!– daily rollover –>-->
<!-- <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.log</fileNamePattern>-->
<!-- </rollingPolicy>-->
<!-- </appender>-->
<!-- <root level="INFO">-->
<!-- <!–<appender-ref ref="CONSOLE"/>–>-->
<!-- <appender-ref ref="ROLLING-FILE"/>-->
<!-- </root>-->
<!--</configuration>-->
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
================================================
FILE: src/main/resources/solidity/contract/HumanStandardToken.sol
================================================
/*
This Token Contract implements the standard token functionality (https://github.com/ethereum/EIPs/issues/20) as well as the following OPTIONAL extras intended for use by humans.
In other words. This is intended for deployment in something like a Token Factory or Mist wallet, and then used by humans.
Imagine coins, currencies, shares, voting weight, etc.
Machine-based, rapid creation of many tokens would not necessarily need these extra features or will be minted in other manners.
1) Initial Finite Supply (upon creation one specifies how much is minted).
2) In the absence of a token registry: Optional Decimal, Symbol & Name.
3) Optional approveAndCall() functionality to notify a contract if an approval() has occurred.
.*/
import "./StandardToken.sol";
pragma solidity ^0.5.2;
contract HumanStandardToken is StandardToken {
function () external {
//if ether is sent to this address, send it back.
revert();
}
/* Public variables of the token */
/*
NOTE:
The following variables are OPTIONAL vanities. One does not have to include them.
They allow one to customise the token contract & in no way influences the core functionality.
Some wallets/interfaces might not even bother to look at this information.
*/
string public name; //fancy name: eg Simon Bucks
uint8 public decimals; //How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether.
string public symbol; //An identifier: eg SBX
string public version = 'H0.1'; //human 0.1 standard. Just an arbitrary versioning scheme.
constructor(
uint256 _initialAmount,
string memory _tokenName,
uint8 _decimalUnits,
string memory _tokenSymbol
) public {
balances[msg.sender] = _initialAmount; // Give the creator all initial tokens
totalSupply = _initialAmount; // Update total supply
name = _tokenName; // Set the name for display purposes
decimals = _decimalUnits; // Amount of decimals for display purposes
symbol = _tokenSymbol; // Set the symbol for display purposes
}
/* Approves and then calls the receiving contract */
function approveAndCall(address _spender, uint256 _value, bytes calldata _extraData) external returns (bool success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
//call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this.
//receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData)
//it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead.
(bool success, ) = _spender.call(abi.encode("receiveApproval(address,uint256,address,bytes)"));
if(!success) { revert(); }
return true;
}
}
================================================
FILE: src/main/resources/solidity/contract/HumanStandardTokenFactory.sol
================================================
import "./HumanStandardToken.sol";
pragma solidity ^0.5.2;
contract HumanStandardTokenFactory {
mapping(address => address[]) public created;
mapping(address => bool) public isHumanToken; //verify without having to do a bytecode check.
bytes public humanStandardByteCode;
function HumanStandardTokenFactory() {
//upon creation of the factory, deploy a HumanStandardToken (parameters are meaningless) and store the bytecode provably.
address verifiedToken = createHumanStandardToken(10000, "Verify Token", 3, "VTX");
humanStandardByteCode = codeAt(verifiedToken);
}
//verifies if a contract that has been deployed is a Human Standard Token.
//NOTE: This is a very expensive function, and should only be used in an eth_call. ~800k gas
function verifyHumanStandardToken(address _tokenContract) returns (bool) {
bytes memory fetchedTokenByteCode = codeAt(_tokenContract);
if (fetchedTokenByteCode.length != humanStandardByteCode.length) {
return false; //clear mismatch
}
//starting iterating through it if lengths match
for (uint i = 0; i < fetchedTokenByteCode.length; i ++) {
if (fetchedTokenByteCode[i] != humanStandardByteCode[i]) {
return false;
}
}
return true;
}
//for now, keeping this internal. Ideally there should also be a live version of this that any contract can use, lib-style.
//retrieves the bytecode at a specific address.
function codeAt(address _addr) internal returns (bytes o_code) {
assembly {
// retrieve the size of the code, this needs assembly
let size := extcodesize(_addr)
// allocate output byte array - this could also be done without assembly
// by using o_code = new bytes(size)
o_code := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(o_code, size)
// actually retrieve the code, this needs assembly
extcodecopy(_addr, add(o_code, 0x20), 0, size)
}
}
function createHumanStandardToken(uint256 _initialAmount, string _name, uint8 _decimals, string _symbol) returns (address) {
HumanStandardToken newToken = (new HumanStandardToken(_initialAmount, _name, _decimals, _symbol));
created[msg.sender].push(address(newToken));
isHumanToken[address(newToken)] = true;
newToken.transfer(msg.sender, _initialAmount); //the factory will own the created tokens. You must transfer them.
return address(newToken);
}
}
================================================
FILE: src/main/resources/solidity/contract/StandardToken.sol
================================================
/*
You should inherit from StandardToken or, for a token like you would want to
deploy in something like Mist, see HumanStandardToken.sol.
(This implements ONLY the standard functions and NOTHING else.
If you deploy this, you won't have anything useful.)
Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/issues/20
.*/
pragma solidity ^0.5.2;
import "./Token.sol";
contract StandardToken is Token {
function transfer(address _to, uint256 _value) public returns (bool success) {
//Default assumes totalSupply can't be over max (2^256 - 1).
//If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap.
//Replace the if with this one instead.
//if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
if (balances[msg.sender] >= _value && _value > 0) {
balances[msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
} else { return false; }
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
//same as above. Replace this line with the following if you want to protect against wrapping uints.
//if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) {
balances[_to] += _value;
balances[_from] -= _value;
allowed[_from][msg.sender] -= _value;
emit Transfer(_from, _to, _value);
return true;
} else { return false; }
}
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
function approve(address _spender, uint256 _value) public returns (bool success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return allowed[_owner][_spender];
}
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
}
================================================
FILE: src/main/resources/solidity/contract/Token.sol
================================================
// Abstract contract for the full ERC 20 Token standard
// https://github.com/ethereum/EIPs/issues/20
pragma solidity ^0.5.2;
contract Token {
/* This is a slight change to the ERC20 base standard.
function totalSupply() constant returns (uint256 supply);
is replaced with:
uint256 public totalSupply;
This automatically creates a getter function for the totalSupply.
This is moved to the base contract since public getter functions are not
currently recognised as an implementation of the matching abstract
function by the compiler.
*/
/// total amount of tokens
uint256 public totalSupply;
/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) public view returns (uint256 balance);
/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _value) public returns (bool success);
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
/// @notice `msg.sender` approves `_spender` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of tokens to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint256 _value) public returns (bool success);
/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender) public view returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
================================================
FILE: src/main/resources/solidity/contract/build/HumanStandardToken.abi
================================================
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_initialAmount","type":"uint256"},{"name":"_tokenName","type":"string"},{"name":"_decimalUnits","type":"uint8"},{"name":"_tokenSymbol","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":false,"stateMutability":"nonpayable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]
================================================
FILE: src/main/resources/solidity/contract/build/StandardToken.abi
================================================
[{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]
================================================
FILE: src/main/resources/solidity/contract/build/Token.abi
================================================
[{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]
================================================
FILE: src/test/java/io/blk/erc20/ControllerIT.java
================================================
package io.blk.erc20;
import java.math.BigInteger;
import java.util.Arrays;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static junit.framework.TestCase.assertNull;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ControllerIT {
// key2
private static final String OTHER_ACCOUNT = "ca843569e3427144cead5e4d5999a3d0ccf92b8e";
// Transaction manager 2
private static final String PRIVATE_FOR = "QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc=";
@Autowired
private NodeConfiguration nodeConfiguration;
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testConfig() {
ResponseEntity<NodeConfiguration> responseEntity =
this.restTemplate.getForEntity("/config", NodeConfiguration.class);
verifyHttpStatus(responseEntity);
assertNotNull(responseEntity.getBody());
}
@Ignore
@Test
public void testLifeCycle() {
Controller.ContractSpecification contractSpecification =
new Controller.ContractSpecification(
BigInteger.valueOf(1000000), "Quorum Token", BigInteger.valueOf(25), "QT");
String contractAddress = deploy(contractSpecification);
verifyName(contractAddress, contractSpecification.getTokenName());
verifySymbol(contractAddress, contractSpecification.getTokenSymbol());
verifyDecimals(contractAddress, contractSpecification.getDecimalUnits());
verifyVersion(contractAddress, "H0.1");
verifyTotalSupply(contractAddress, contractSpecification.getInitialAmount());
verifyBalanceOf(contractAddress,
nodeConfiguration.getFromAddress(), contractSpecification.getInitialAmount());
Controller.ApproveRequest approveRequest = new Controller.ApproveRequest(
OTHER_ACCOUNT, BigInteger.valueOf(10000));
verifyApproveTx(contractAddress, approveRequest);
verifyAllowance(
contractAddress, nodeConfiguration.getFromAddress(), OTHER_ACCOUNT,
approveRequest.getValue());
Controller.TransferRequest transferRequest = new Controller.TransferRequest(
OTHER_ACCOUNT, BigInteger.valueOf(10000));
verifyTransferTx(contractAddress, transferRequest);
verifyBalanceOf(
contractAddress,
transferRequest.getTo(),
transferRequest.getValue());
verifyBalanceOf(
contractAddress,
nodeConfiguration.getFromAddress(),
contractSpecification.getInitialAmount().subtract(transferRequest.getValue()));
// Needs to be performed by another account, hence this will fail
Controller.TransferFromRequest transferFromRequest =
new Controller.TransferFromRequest(
nodeConfiguration.getFromAddress(), OTHER_ACCOUNT, BigInteger.valueOf(1000));
verifyTransferFromTxFailure(contractAddress, transferFromRequest);
// Therefore our balance remains the same
verifyBalanceOf(
contractAddress,
transferFromRequest.getFrom(),
contractSpecification.getInitialAmount().subtract(transferRequest.getValue()));
}
private String deploy(
Controller.ContractSpecification contractSpecification) {
ResponseEntity<String> responseEntity =
this.restTemplate.postForEntity(
"/deploy", buildEntity(contractSpecification), String.class);
verifyHttpStatus(responseEntity);
String contractAddress = responseEntity.getBody();
assertFalse(contractAddress.isEmpty());
return contractAddress;
}
private void verifyName(String contractAddress, String name) {
ResponseEntity<String> responseEntity =
this.restTemplate.getForEntity(
"/" + contractAddress + "" + "/name", String.class);
verifyHttpStatus(responseEntity);
assertThat(responseEntity.getBody(), is(name));
}
private void verifyTotalSupply(String contractAddress, BigInteger totalSupply) {
ResponseEntity<BigInteger> responseEntity =
this.restTemplate.getForEntity(
"/" + contractAddress + "" + "/totalSupply", BigInteger.class);
verifyHttpStatus(responseEntity);
assertThat(responseEntity.getBody(), is(totalSupply));
}
private void verifyDecimals(String contractAddress, BigInteger decimals) {
ResponseEntity<BigInteger> responseEntity =
this.restTemplate.getForEntity(
"/" + contractAddress + "" + "/decimals", BigInteger.class);
verifyHttpStatus(responseEntity);
assertThat(responseEntity.getBody(), is(decimals));
}
private void verifyVersion(String contractAddress, String version) {
ResponseEntity<String> responseEntity =
this.restTemplate.getForEntity(
"/" + contractAddress + "" + "/version", String.class);
verifyHttpStatus(responseEntity);
assertThat(responseEntity.getBody(), is(version));
}
private void verifyBalanceOf(String contractAddress, String ownerAddress, BigInteger balance) {
ResponseEntity<BigInteger> responseEntity =
this.restTemplate.getForEntity(
"/" + contractAddress + "" + "/balanceOf/" + ownerAddress,
BigInteger.class);
verifyHttpStatus(responseEntity);
assertThat(responseEntity.getBody(), is(balance));
}
private void verifySymbol(String contractAddress, String symbol) {
ResponseEntity<String> responseEntity =
this.restTemplate.getForEntity(
"/" + contractAddress + "" + "/symbol", String.class);
verifyHttpStatus(responseEntity);
assertThat(responseEntity.getBody(), is(symbol));
}
private void verifyAllowance(
String contractAddress,
String ownerAddress,
String spenderAddress,
BigInteger expected) {
ResponseEntity<BigInteger> responseEntity =
this.restTemplate.getForEntity(
"/" + contractAddress
+ "/allowance?"
+ "ownerAddress={ownerAddress}"
+ "&spenderAddress={spenderAddress}",
BigInteger.class,
ownerAddress,
spenderAddress);
verifyHttpStatus(responseEntity);
assertThat(responseEntity.getBody(), is(expected));
}
private void verifyTransferFromTxFailure(
String contractAddress, Controller.TransferFromRequest transferFromRequest) {
ResponseEntity<TransactionResponse> responseEntity =
this.restTemplate.postForEntity(
"/" + contractAddress + "/transferFrom",
buildEntity(transferFromRequest),
TransactionResponse.class);
verifyPostResponseFailure(responseEntity);
}
private void verifyApproveTx(
String contractAddress, Controller.ApproveRequest approveRequest) {
ResponseEntity<TransactionResponse> responseEntity =
this.restTemplate.postForEntity(
"/" + contractAddress + "/approve",
buildEntity(approveRequest),
TransactionResponse.class);
verifyPostResponse(responseEntity);
}
private void verifyTransferTx(
String contractAddress, Controller.TransferRequest transferRequest) {
ResponseEntity<TransactionResponse> responseEntity =
this.restTemplate.postForEntity(
"/" + contractAddress + "/transfer",
buildEntity(transferRequest),
TransactionResponse.class);
verifyPostResponse(responseEntity);
}
private <T> HttpEntity<T> buildEntity(T body) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("privateFor", PRIVATE_FOR);
return new HttpEntity<>(body, headers);
}
private <T> void verifyHttpStatus(ResponseEntity<T> responseEntity) {
assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
}
private void verifyPostResponse(ResponseEntity<TransactionResponse> responseEntity) {
verifyPostResponseBody(responseEntity);
assertNotNull(responseEntity.getBody().getEvent());
}
private void verifyPostResponseFailure(ResponseEntity<TransactionResponse> responseEntity) {
assertNull(responseEntity.getBody().getEvent());
}
private void verifyPostResponseBody(ResponseEntity<TransactionResponse> responseEntity) {
verifyHttpStatus(responseEntity);
TransactionResponse body = responseEntity.getBody();
assertNotNull(body);
String transactionHash = body.getTransactionHash();
assertTrue(transactionHash.startsWith("0x"));
assertThat(transactionHash.length(), is(66));
}
}
================================================
FILE: src/test/resources/logback-test.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- Prevent Spring trying to log to /tmp/spring.log on Linux hosts. This needs to be before
the defaults include -->
<property name="LOG_TEMP" value="./logs" />
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.web3j" level="debug" />
<logger name="org.apache.http.wire" level="debug" />
<root level="INFO" />
</configuration>
gitextract_nychjct6/
├── .gitignore
├── README.md
├── build.gradle
├── docker/
│ └── Dockerfile
├── generate.sh
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src/
├── main/
│ ├── java/
│ │ └── io/
│ │ └── blk/
│ │ └── erc20/
│ │ ├── Application.java
│ │ ├── ContractService.java
│ │ ├── Controller.java
│ │ ├── NodeConfiguration.java
│ │ ├── TransactionResponse.java
│ │ └── generated/
│ │ └── HumanStandardToken.java
│ └── resources/
│ ├── config/
│ │ └── application.yml
│ ├── logback.xml
│ └── solidity/
│ └── contract/
│ ├── HumanStandardToken.sol
│ ├── HumanStandardTokenFactory.sol
│ ├── StandardToken.sol
│ ├── Token.sol
│ └── build/
│ ├── HumanStandardToken.abi
│ ├── StandardToken.abi
│ └── Token.abi
└── test/
├── java/
│ └── io/
│ └── blk/
│ └── erc20/
│ └── ControllerIT.java
└── resources/
└── logback-test.xml
SYMBOL INDEX (159 symbols across 7 files)
FILE: src/main/java/io/blk/erc20/Application.java
class Application (line 25) | @EnableSwagger2
method main (line 29) | public static void main(String[] args) {
method quorum (line 38) | @Bean
method lenderApi (line 54) | @Bean
method apiInfo (line 65) | private ApiInfo apiInfo() {
FILE: src/main/java/io/blk/erc20/ContractService.java
class ContractService (line 28) | @Service
method ContractService (line 35) | @Autowired
method getConfig (line 41) | public NodeConfiguration getConfig() {
method deploy (line 45) | public String deploy(
method name (line 61) | public String name(String contractAddress) throws Exception {
method approve (line 70) | public TransactionResponse<ApprovalEventResponse> approve(
method totalSupply (line 82) | public String totalSupply(String contractAddress) throws Exception {
method transferFrom (line 91) | public TransactionResponse<TransferEventResponse> transferFrom(
method decimals (line 103) | public String decimals(String contractAddress) throws Exception {
method version (line 112) | public String version(String contractAddress) throws Exception {
method balanceOf (line 121) | public String balanceOf(String contractAddress, String ownerAddress) t...
method symbol (line 130) | public String symbol(String contractAddress) throws Exception {
method transfer (line 139) | public TransactionResponse<TransferEventResponse> transfer(
method approveAndCall (line 151) | public TransactionResponse<ApprovalEventResponse> approveAndCall(
method allowance (line 167) | public String allowance(String contractAddress, String ownerAddress, S...
method load (line 178) | private HumanStandardToken load(String contractAddress, List<String> p...
method load (line 185) | private HumanStandardToken load(String contractAddress) {
method processApprovalEventResponse (line 192) | private TransactionResponse<ApprovalEventResponse>
method processTransferEventsResponse (line 203) | private TransactionResponse<TransferEventResponse>
method processEventResponse (line 214) | private <T, R> TransactionResponse<R> processEventResponse(
class TransferEventResponse (line 226) | @Getter
method TransferEventResponse (line 233) | public TransferEventResponse() { }
method TransferEventResponse (line 235) | public TransferEventResponse(
method getFrom (line 242) | public String getFrom() {
method setFrom (line 246) | public void setFrom(String from) {
method getTo (line 250) | public String getTo() {
method setTo (line 254) | public void setTo(String to) {
method getValue (line 258) | public long getValue() {
method setValue (line 262) | public void setValue(long value) {
class ApprovalEventResponse (line 267) | @Getter
method ApprovalEventResponse (line 274) | public ApprovalEventResponse() { }
method ApprovalEventResponse (line 276) | public ApprovalEventResponse(
method getOwner (line 283) | public String getOwner() {
method setOwner (line 287) | public void setOwner(String owner) {
method getSpender (line 291) | public String getSpender() {
method setSpender (line 295) | public void setSpender(String spender) {
method getValue (line 299) | public long getValue() {
method setValue (line 303) | public void setValue(long value) {
FILE: src/main/java/io/blk/erc20/Controller.java
class Controller (line 26) | @Api("ERC-20 token standard API")
method Controller (line 32) | @Autowired
method config (line 37) | @ApiOperation("Application configuration")
method deploy (line 44) | @ApiOperation(
method name (line 65) | @ApiOperation("Get token name")
method approve (line 71) | @ApiOperation(
method totalSupply (line 91) | @ApiOperation("Get total supply of tokens")
method transferFrom (line 97) | @ApiOperation(
method decimals (line 118) | @ApiOperation("Get decimal precision of tokens")
method version (line 124) | @ApiOperation("Get contract version")
method balanceOf (line 130) | @ApiOperation("Get token balance for address")
method symbol (line 139) | @ApiOperation("Get token symbol")
method transfer (line 145) | @ApiOperation(
method approveAndCall (line 165) | @ApiOperation(
method allowance (line 187) | @ApiOperation("Get quantity of tokens you can transfer on another toke...
method extractPrivateFor (line 197) | private static @Nullable List<String> extractPrivateFor(HttpServletReq...
class ContractSpecification (line 206) | @Data
method ContractSpecification (line 213) | ContractSpecification() {
method ContractSpecification (line 216) | ContractSpecification(BigInteger initialAmount, String tokenName, Bi...
method getDecimalUnits (line 223) | public BigInteger getDecimalUnits() {
method getInitialAmount (line 227) | public BigInteger getInitialAmount() {
method getTokenName (line 231) | public String getTokenName() {
method getTokenSymbol (line 235) | public String getTokenSymbol() {
class ApproveRequest (line 240) | @Data
method ApproveRequest (line 245) | ApproveRequest() {}
method ApproveRequest (line 247) | ApproveRequest(String spender, BigInteger value) {
method getSpender (line 252) | public String getSpender() {
method getValue (line 256) | public BigInteger getValue() {
class TransferFromRequest (line 261) | @Data
method TransferFromRequest (line 267) | TransferFromRequest() {}
method TransferFromRequest (line 269) | TransferFromRequest(String from, String to, BigInteger value) {
method getFrom (line 276) | public String getFrom() {
method getValue (line 280) | BigInteger getValue() {
method getTo (line 284) | public String getTo() {
class TransferRequest (line 289) | @Data
method TransferRequest (line 294) | TransferRequest(String to, BigInteger value) {
method TransferRequest (line 299) | TransferRequest() {}
method getTo (line 301) | public String getTo() {
method getValue (line 305) | public BigInteger getValue() {
class ApproveAndCallRequest (line 310) | @Data
method ApproveAndCallRequest (line 316) | ApproveAndCallRequest() {}
method ApproveAndCallRequest (line 318) | ApproveAndCallRequest(String spender, BigInteger value, String extra...
method getSpender (line 324) | String getSpender() {
method getValue (line 328) | BigInteger getValue() {
method getExtraData (line 332) | String getExtraData() {
class AllowanceRequest (line 337) | @Data
method AllowanceRequest (line 342) | AllowanceRequest() {}
method AllowanceRequest (line 344) | AllowanceRequest(String ownerAddress, String spenderAddress) {
method getOwnerAddress (line 349) | public String getOwnerAddress() {
method setOwnerAddress (line 353) | public void setOwnerAddress(String ownerAddress) {
method getSpenderAddress (line 357) | public String getSpenderAddress() {
method setSpenderAddress (line 361) | public void setSpenderAddress(String spenderAddress) {
FILE: src/main/java/io/blk/erc20/NodeConfiguration.java
class NodeConfiguration (line 10) | @Data
method getNodeEndpoint (line 18) | public String getNodeEndpoint() {
method setNodeEndpoint (line 22) | public void setNodeEndpoint(String nodeEndpoint) {
method getFromAddress (line 26) | public String getFromAddress() {
method setFromAddress (line 30) | public void setFromAddress(String fromAddress) {
FILE: src/main/java/io/blk/erc20/TransactionResponse.java
class TransactionResponse (line 9) | @Getter
method TransactionResponse (line 16) | TransactionResponse() { }
method TransactionResponse (line 18) | public TransactionResponse(String transactionHash) {
method TransactionResponse (line 22) | public TransactionResponse(String transactionHash, T event) {
method getTransactionHash (line 27) | public String getTransactionHash() {
method setTransactionHash (line 31) | public void setTransactionHash(String transactionHash) {
method getEvent (line 35) | public T getEvent() {
method setEvent (line 39) | public void setEvent(T event) {
FILE: src/main/java/io/blk/erc20/generated/HumanStandardToken.java
class HumanStandardToken (line 39) | public class HumanStandardToken extends Contract {
method HumanStandardToken (line 72) | @Deprecated
method HumanStandardToken (line 77) | protected HumanStandardToken(String contractAddress, Web3j web3j, Cred...
method HumanStandardToken (line 81) | @Deprecated
method HumanStandardToken (line 86) | protected HumanStandardToken(String contractAddress, Web3j web3j, Tran...
method name (line 90) | public RemoteCall<String> name() {
method approve (line 97) | public RemoteCall<TransactionReceipt> approve(String _spender, BigInte...
method totalSupply (line 106) | public RemoteCall<BigInteger> totalSupply() {
method transferFrom (line 113) | public RemoteCall<TransactionReceipt> transferFrom(String _from, Strin...
method decimals (line 123) | public RemoteCall<BigInteger> decimals() {
method version (line 130) | public RemoteCall<String> version() {
method balanceOf (line 137) | public RemoteCall<BigInteger> balanceOf(String _owner) {
method symbol (line 144) | public RemoteCall<String> symbol() {
method transfer (line 151) | public RemoteCall<TransactionReceipt> transfer(String _to, BigInteger ...
method approveAndCall (line 160) | public RemoteCall<TransactionReceipt> approveAndCall(String _spender, ...
method allowance (line 170) | public RemoteCall<BigInteger> allowance(String _owner, String _spender) {
method getTransferEvents (line 178) | public List<TransferEventResponse> getTransferEvents(TransactionReceip...
method transferEventFlowable (line 192) | public Flowable<TransferEventResponse> transferEventFlowable(EthFilter...
method transferEventFlowable (line 207) | public Flowable<TransferEventResponse> transferEventFlowable(DefaultBl...
method getApprovalEvents (line 213) | public List<ApprovalEventResponse> getApprovalEvents(TransactionReceip...
method approvalEventFlowable (line 227) | public Flowable<ApprovalEventResponse> approvalEventFlowable(EthFilter...
method approvalEventFlowable (line 242) | public Flowable<ApprovalEventResponse> approvalEventFlowable(DefaultBl...
method load (line 248) | @Deprecated
method load (line 253) | @Deprecated
method load (line 258) | public static HumanStandardToken load(String contractAddress, Web3j we...
method load (line 262) | public static HumanStandardToken load(String contractAddress, Web3j we...
method deploy (line 266) | public static RemoteCall<HumanStandardToken> deploy(Web3j web3j, Crede...
method deploy (line 274) | public static RemoteCall<HumanStandardToken> deploy(Web3j web3j, Trans...
method deploy (line 282) | @Deprecated
method deploy (line 291) | @Deprecated
class TransferEventResponse (line 300) | public static class TransferEventResponse {
class ApprovalEventResponse (line 310) | public static class ApprovalEventResponse {
FILE: src/test/java/io/blk/erc20/ControllerIT.java
class ControllerIT (line 26) | @RunWith(SpringJUnit4ClassRunner.class)
method testConfig (line 43) | @Test
method testLifeCycle (line 51) | @Ignore
method deploy (line 101) | private String deploy(
method verifyName (line 114) | private void verifyName(String contractAddress, String name) {
method verifyTotalSupply (line 122) | private void verifyTotalSupply(String contractAddress, BigInteger tota...
method verifyDecimals (line 130) | private void verifyDecimals(String contractAddress, BigInteger decimal...
method verifyVersion (line 138) | private void verifyVersion(String contractAddress, String version) {
method verifyBalanceOf (line 146) | private void verifyBalanceOf(String contractAddress, String ownerAddre...
method verifySymbol (line 155) | private void verifySymbol(String contractAddress, String symbol) {
method verifyAllowance (line 163) | private void verifyAllowance(
method verifyTransferFromTxFailure (line 181) | private void verifyTransferFromTxFailure(
method verifyApproveTx (line 191) | private void verifyApproveTx(
method verifyTransferTx (line 201) | private void verifyTransferTx(
method buildEntity (line 211) | private <T> HttpEntity<T> buildEntity(T body) {
method verifyHttpStatus (line 220) | private <T> void verifyHttpStatus(ResponseEntity<T> responseEntity) {
method verifyPostResponse (line 224) | private void verifyPostResponse(ResponseEntity<TransactionResponse> re...
method verifyPostResponseFailure (line 229) | private void verifyPostResponseFailure(ResponseEntity<TransactionRespo...
method verifyPostResponseBody (line 233) | private void verifyPostResponseBody(ResponseEntity<TransactionResponse...
Condensed preview — 27 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (101K chars).
[
{
"path": ".gitignore",
"chars": 676,
"preview": "# Created by .ignore support plugin (hsz.mobi)\n### Java template\n*.class\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Pa"
},
{
"path": "README.md",
"chars": 2854,
"preview": "# ERC-20 RESTful service\n\nThis application provides a RESTful service for creating and managing \n[ERC-20 tokens](https:/"
},
{
"path": "build.gradle",
"chars": 1348,
"preview": "plugins {\n id 'java'\n id 'idea'\n id 'eclipse'\n id 'application'\n id 'org.springframework.boot' version '2"
},
{
"path": "docker/Dockerfile",
"chars": 880,
"preview": "FROM frolvlad/alpine-java\n\nRUN apk update && apk upgrade && \\\n apk add --no-cache bash git openssh\n\nRUN mkdir -p /app"
},
{
"path": "generate.sh",
"chars": 339,
"preview": "#!/usr/bin/env bash\n\ncd src/main/resources/solidity/contract/ && \\\n solc --bin --abi --optimize --overwrite HumanStan"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 232,
"preview": "#Mon Nov 18 15:02:41 GMT 2019\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-5.1.1-all.zip\ndistributi"
},
{
"path": "gradlew",
"chars": 5960,
"preview": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0"
},
{
"path": "gradlew.bat",
"chars": 2942,
"preview": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (th"
},
{
"path": "settings.gradle",
"chars": 40,
"preview": "rootProject.name = 'erc20-rest-service'\n"
},
{
"path": "src/main/java/io/blk/erc20/Application.java",
"chars": 2606,
"preview": "package io.blk.erc20;\n\nimport com.google.common.base.Predicates;\nimport org.springframework.beans.factory.annotation.Aut"
},
{
"path": "src/main/java/io/blk/erc20/ContractService.java",
"chars": 11134,
"preview": "package io.blk.erc20;\n\nimport java.math.BigInteger;\nimport java.util.Collections;\nimport java.util.List;\nimport java.uti"
},
{
"path": "src/main/java/io/blk/erc20/Controller.java",
"chars": 12677,
"preview": "package io.blk.erc20;\n\nimport java.math.BigInteger;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.u"
},
{
"path": "src/main/java/io/blk/erc20/NodeConfiguration.java",
"chars": 787,
"preview": "package io.blk.erc20;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\ni"
},
{
"path": "src/main/java/io/blk/erc20/TransactionResponse.java",
"chars": 825,
"preview": "package io.blk.erc20;\n\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * TransactionResponse wrapper.\n */\n@Getter\n@Set"
},
{
"path": "src/main/java/io/blk/erc20/generated/HumanStandardToken.java",
"chars": 22002,
"preview": "package io.blk.erc20.generated;\n\nimport io.reactivex.Flowable;\nimport java.math.BigInteger;\nimport java.util.ArrayList;\n"
},
{
"path": "src/main/resources/config/application.yml",
"chars": 483,
"preview": "# Port to run on\nserver:\n port: ${port:8081}\n\n# Our log file path and name\nlogging:\n file: logs/erc20-rest-service.log"
},
{
"path": "src/main/resources/logback.xml",
"chars": 1893,
"preview": "<!--<?xml version=\"1.0\" encoding=\"UTF-8\"?>-->\n<!--<configuration>-->\n<!-- <!– Prevent Spring trying to log t"
},
{
"path": "src/main/resources/solidity/contract/HumanStandardToken.sol",
"chars": 3241,
"preview": "/*\nThis Token Contract implements the standard token functionality (https://github.com/ethereum/EIPs/issues/20) as well "
},
{
"path": "src/main/resources/solidity/contract/HumanStandardTokenFactory.sol",
"chars": 2669,
"preview": "import \"./HumanStandardToken.sol\";\n\npragma solidity ^0.5.2;\n\ncontract HumanStandardTokenFactory {\n\n mapping(address ="
},
{
"path": "src/main/resources/solidity/contract/StandardToken.sol",
"chars": 2354,
"preview": "/*\nYou should inherit from StandardToken or, for a token like you would want to\ndeploy in something like Mist, see Human"
},
{
"path": "src/main/resources/solidity/contract/Token.sol",
"chars": 2389,
"preview": "// Abstract contract for the full ERC 20 Token standard\n// https://github.com/ethereum/EIPs/issues/20\npragma solidity ^0"
},
{
"path": "src/main/resources/solidity/contract/build/HumanStandardToken.abi",
"chars": 2878,
"preview": "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"v"
},
{
"path": "src/main/resources/solidity/contract/build/StandardToken.abi",
"chars": 1716,
"preview": "[{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\""
},
{
"path": "src/main/resources/solidity/contract/build/Token.abi",
"chars": 1716,
"preview": "[{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\""
},
{
"path": "src/test/java/io/blk/erc20/ControllerIT.java",
"chars": 10145,
"preview": "package io.blk.erc20;\n\nimport java.math.BigInteger;\nimport java.util.Arrays;\n\nimport org.junit.Ignore;\nimport org.junit."
},
{
"path": "src/test/resources/logback-test.xml",
"chars": 451,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n <!-- Prevent Spring trying to log to /tmp/spring.log on Linux"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the blk-io/erc20-rest-service GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 27 files (93.0 KB), approximately 22.6k tokens, and a symbol index with 159 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.