text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Creating relayer configuration..."
mkdir -p ~/.near-credentials/testnet
echo $TEST_NET_KEY > ~/.near-credentials/testnet/aurora-relayer-ci.testnet.json
cp ~/.near-credentials/testnet/aurora-relayer-ci.testnet.json $REPO_ROOT/config/aurora-relayer-ci.testnet.json

rm $REPO_ROOT/config/local.yaml || true
cat >$REPO_ROOT/config/local.yaml <<EOF
---
port: 8545
database: postgres://aurora:aurora@${DATABASE_CONTAINER_NAME}/aurora
broker:
network: testnet
endpoint: https://archival-rpc.testnet.near.org
engine: aurora
signer: aurora-relayer-ci.testnet
signerKey: config/aurora-relayer-ci.testnet.json
writable: true
debug: false
verbose: false
force: false
EOF


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Starting database..."
params=()
if [[ -z $RUNNER_NAME ]]; then
    params+=(-p)
    params+=(5432:5432)
fi
docker run -d \
    --restart unless-stopped \
    --network $NETWORK_NAME \
    --name $DATABASE_CONTAINER_NAME \
    "${params[@]}" \
    $DATABASE_IMAGE_NAME

echo "Starting indexer..."
docker run -d --init \
    --restart unless-stopped \
    --network $NETWORK_NAME \
    -e WAIT_HOSTS=${DATABASE_CONTAINER_NAME}:5432 \
    -e WAIT_BEFORE=1 \
    -e NEAR_ENV=testnet \
    -e NODE_ENV=testnet \
    -v $REPO_ROOT/config:/srv/aurora/relayer/config \
    --name $INDEXER_CONTAINER_NAME \
    $ENDPOINT_IMAGE_NAME \
    sh -c "util/indexer/indexer --config config/local.yaml | node lib/indexer_backend.js"

echo "Starting endpoint..."
params=()
if [[ -z $RUNNER_NAME ]]; then
    params+=(-p)
    params+=(8545:8545)
fi
docker run -d --init \
    --restart unless-stopped \
    --network $NETWORK_NAME \
    -e WAIT_HOSTS=${DATABASE_CONTAINER_NAME}:5432 \
    -e WAIT_BEFORE=1 \
    -e NEAR_ENV=testnet \
    -e NODE_ENV=testnet \
    -v $REPO_ROOT/config:/srv/aurora/relayer/config \
    --name $ENDPOINT_CONTAINER_NAME \
    "${params[@]}" \
    $ENDPOINT_IMAGE_NAME \
    node lib/index.js

repo_name: aurora-is-near/aurora-relayer

repo_language: TypeScript

mime_type: text/plain


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Building indexer binary..."
cd util/indexer
make
cd ../..

echo "Building database image..."
time docker build -t $RAW_DATABASE_IMAGE_NAME -f $REPO_ROOT/.docker/Dockerfile.database $REPO_ROOT/.
time docker build -t $DATABASE_IMAGE_NAME --build-arg RAW_DATABASE_IMAGE_NAME=$RAW_DATABASE_IMAGE_NAME -f $REPO_ROOT/.ci/e2e/controls/relayer/Dockerfile.database $REPO_ROOT/.

echo "Building endpoint image..."
time docker build -t $ENDPOINT_IMAGE_NAME -f $REPO_ROOT/.docker/Dockerfile.endpoint $REPO_ROOT/.


text:
ARG RAW_DATABASE_IMAGE_NAME
FROM ${RAW_DATABASE_IMAGE_NAME}

COPY test/fixtures/mocks_init.sh /docker-entrypoint-initdb.d/


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

export NEAR_ENV=local

echo "Creating NEAR account..."
rm ~/.near-credentials/local/test.near.json || true
rm ~/.near-credentials/local/aurora.test.near.json || true
$WORKDIR/near-cli/bin/near create-account aurora.test.near \
    --master-account=test.near \
    --initial-balance 1000000 \
    --key-path $WORKDIR/nearData/validator_key.json \
    --node-url http://${NEARCORE_HOST}:3030

rm $WORKDIR/aurora.test.near.json || true
chmod 777 ~/.near-credentials/local/aurora.test.near.json
cp ~/.near-credentials/local/aurora.test.near.json $WORKDIR/aurora.test.near.json


text:
#!/bin/bash -e

RUNNER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $RUNNER_DIR/../controls/common.sh

rm -rf $WORKDIR || true

echo "Restoring workdir from cache..."
cache-util restore relayer-ci-workdir-${COMMIT_HASH}:${WORKDIR} || true

$RUNNER_DIR/../setup.sh --start


text:
#!/bin/bash -e

RUNNER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $RUNNER_DIR/../controls/common.sh

$RUNNER_DIR/../stop.sh
rm -rf $WORKDIR || true


text:
#!/bin/bash -e

RUNNER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $RUNNER_DIR/../controls/common.sh

$RUNNER_DIR/../stop.sh --clean


text:
#!/bin/bash -e

RUNNER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $RUNNER_DIR/../controls/common.sh

rm -rf $WORKDIR || true
$RUNNER_DIR/../setup.sh

echo "Saving workdir to cache..."
cache-util msave relayer-ci-workdir-${COMMIT_HASH}:${WORKDIR} || true
rm -rf $WORKDIR || true


text:
#!/bin/bash -e

CONTROLS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
WORKDIR=$CONTROLS_DIR/../workdir
REPO_ROOT=$CONTROLS_DIR/../..

if [[ ! -z $RUNNER_NAME ]]; then
    # All CI-runners are docker containers, and creating new
    # Docker objects (images, containers, etc) from some runner
    # will make them appear on host-level Docker, since
    # socket-binding solution is used for Docker-in-Docker.
    #
    # To avoid collision of such objects, $RUNNER_NAME suffix
    # is added to every object's name.

    UNIQUE_KEY=$(echo $RUNNER_NAME | tr '[:upper:]' '[:lower:]')
    COMMIT_HASH=$(git rev-parse HEAD)

    NETWORK_NAME=network-for-$UNIQUE_KEY
    DATABASE_IMAGE_NAME=relayer-database-img-$COMMIT_HASH
    ENDPOINT_IMAGE_NAME=relayer-endpoint-img-$COMMIT_HASH
    DATABASE_CONTAINER_NAME=relayer-database-$UNIQUE_KEY
    INDEXER_CONTAINER_NAME=relayer-indexer-$UNIQUE_KEY
    ENDPOINT_CONTAINER_NAME=relayer-endpoint-$UNIQUE_KEY
    NEARCORE_CONTAINER_NAME=nearcore-$UNIQUE_KEY
else
    NETWORK_NAME=relayer-testing-network
    DATABASE_IMAGE_NAME=relayer-testing-database-img
    ENDPOINT_IMAGE_NAME=relayer-testing-endpoint-img
    DATABASE_CONTAINER_NAME=relayer-testing-database
    INDEXER_CONTAINER_NAME=relayer-testing-indexer
    ENDPOINT_CONTAINER_NAME=relayer-testing-endpoint
    NEARCORE_CONTAINER_NAME=relayer-testing-nearcore
fi

# Version locks.
# Specific versions of each dependency are used
# in order to provide consistent CI.

NEAR_CLI_HEAD=f96735433ac70e19d58823f0d2b9775675fc4dbc
# Commit hash from: https://github.com/near/near-cli/commits/master

AURORA_CLI_HEAD=7f7c2d29c5114db2b6530a6c0fa09b2068f217df
# Commit hash from: https://github.com/aurora-is-near/aurora-cli/commits/master

NEARCORE_TAG=master-6a4cffa70383c1d91b2abeedad553da7e25f067b
# Docker image tag from: https://hub.docker.com/r/nearprotocol/nearcore/tags

CONTRACT_URL=https://github.com/aurora-is-near/aurora-engine/releases/download/2.1.0/mainnet-release.wasm
# Download url from: https://github.com/aurora-is-near/aurora-engine/releases


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Removing inner network..."
if [[ ! -z $RUNNER_NAME ]]; then
    docker network disconnect -f $NETWORK_NAME $RUNNER_NAME || true
fi
docker network rm $NETWORK_NAME || true


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Establishing inner network..."
docker network create -d bridge $NETWORK_NAME
if [[ ! -z $RUNNER_NAME ]]; then
    docker network connect $NETWORK_NAME $RUNNER_NAME
fi


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Initializing nearcore..."
rm -rf $WORKDIR/nearData || true
mkdir $WORKDIR/nearData
docker run --rm \
    -v $WORKDIR/nearData:/srv/near \
    --name $NEARCORE_CONTAINER_NAME \
    nearprotocol/nearcore:$NEARCORE_TAG \
    neard --home=/srv/near init


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Stopping nearcore..."
docker stop $NEARCORE_CONTAINER_NAME || true
docker rm -f $NEARCORE_CONTAINER_NAME || true


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Starting nearcore..."
params=()
if [[ -z $RUNNER_NAME ]]; then
    params+=(-p)
    params+=(3030:3030)
fi
docker run -d \
    --restart unless-stopped \
    -v $WORKDIR/nearData:/srv/near \
    --network $NETWORK_NAME \
    --name $NEARCORE_CONTAINER_NAME \
    "${params[@]}" \
    nearprotocol/nearcore:$NEARCORE_TAG \
    neard --home=/srv/near run


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Installing near-cli..."
cd $WORKDIR
rm -rf near-cli || true
if [[ ! -z $RUNNER_NAME ]]; then
    cache-util restore near-cli-${NEAR_CLI_HEAD}:${WORKDIR}/near-cli || true
fi
if [[ ! -d near-cli ]]; then
    git clone https://github.com/near/near-cli.git
    cd near-cli
    git checkout -q $NEAR_CLI_HEAD
    time npm install
    if [[ ! -z $RUNNER_NAME ]]; then
        cache-util save near-cli-${NEAR_CLI_HEAD}:${WORKDIR}/near-cli || true
    fi
fi

echo "Installing aurora-cli..."
cd $WORKDIR
rm -rf aurora-cli || true
if [[ ! -z $RUNNER_NAME ]]; then
    cache-util restore near-cli-${AURORA_CLI_HEAD}:${WORKDIR}/aurora-cli || true
fi
if [[ ! -d aurora-cli ]]; then
    git clone https://github.com/aurora-is-near/aurora-cli.git
    cd aurora-cli
    git checkout -q $AURORA_CLI_HEAD
    time npm install
    if [[ ! -z $RUNNER_NAME ]]; then
        cache-util save near-cli-${AURORA_CLI_HEAD}:${WORKDIR}/aurora-cli || true
    fi
fi


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Removing database and endpoint images..."
docker rmi -f $DATABASE_IMAGE_NAME $ENDPOINT_IMAGE_NAME || true


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Removing endpoint, indexer and database containers..."
docker rm -f \
    $ENDPOINT_CONTAINER_NAME \
    $INDEXER_CONTAINER_NAME \
    $DATABASE_CONTAINER_NAME \
    || true


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Stopping endpoint, indexer and database containers..."
docker stop \
    $ENDPOINT_CONTAINER_NAME \
    $INDEXER_CONTAINER_NAME \
    $DATABASE_CONTAINER_NAME \
    || true


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Creating relayer configuration..."
rm $REPO_ROOT/config/aurora.test.near.json || true
cp $WORKDIR/aurora.test.near.json $REPO_ROOT/config/aurora.test.near.json
rm $REPO_ROOT/config/local.yaml || true
cat >$REPO_ROOT/config/local.yaml <<EOF
---
port: 8545
database: postgres://aurora:aurora@${DATABASE_CONTAINER_NAME}/aurora
broker:
network: local
endpoint: http://${NEARCORE_CONTAINER_NAME}:3030
engine: aurora.test.near
signer: aurora.test.near
signerKey: config/aurora.test.near.json
writable: true
debug: false
verbose: false
force: false
EOF


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Starting database..."
params=()
if [[ -z $RUNNER_NAME ]]; then
    params+=(-p)
    params+=(5432:5432)
fi
docker run -d \
    --restart unless-stopped \
    --network $NETWORK_NAME \
    --name $DATABASE_CONTAINER_NAME \
    "${params[@]}" \
    $DATABASE_IMAGE_NAME

echo "Starting indexer..."
docker run -d --init \
    --restart unless-stopped \
    --network $NETWORK_NAME \
    -e WAIT_HOSTS=${DATABASE_CONTAINER_NAME}:5432 \
    -e WAIT_BEFORE=1 \
    -e NEAR_ENV=localnet \
    -e NODE_ENV=localnet \
    -v $REPO_ROOT/config:/srv/aurora/relayer/config \
    --name $INDEXER_CONTAINER_NAME \
    $ENDPOINT_IMAGE_NAME \
    sh -c "util/indexer/indexer --config config/local.yaml | node lib/indexer_backend.js"

echo "Starting endpoint..."
params=()
if [[ -z $RUNNER_NAME ]]; then
    params+=(-p)
    params+=(8545:8545)
fi
docker run -d --init \
    --restart unless-stopped \
    --network $NETWORK_NAME \
    -e WAIT_HOSTS=${DATABASE_CONTAINER_NAME}:5432 \
    -e WAIT_BEFORE=1 \
    -e NEAR_ENV=localnet \
    -e NODE_ENV=localnet \
    -v $REPO_ROOT/config:/srv/aurora/relayer/config \
    --name $ENDPOINT_CONTAINER_NAME \
    "${params[@]}" \
    $ENDPOINT_IMAGE_NAME \
    node lib/index.js


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

echo "Building database image..."
time docker build -t $DATABASE_IMAGE_NAME -f $REPO_ROOT/.docker/Dockerfile.database $REPO_ROOT/.

echo "Building endpoint image..."
time docker build -t $ENDPOINT_IMAGE_NAME -f $REPO_ROOT/.docker/Dockerfile.endpoint $REPO_ROOT/.


text:
#!/bin/bash -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $SCRIPT_DIR/../common.sh

export NEAR_ENV=local

NEARCORE_HOST=$NEARCORE_CONTAINER_NAME
if [[ -z $RUNNER_NAME ]]; then
    NEARCORE_HOST=localhost
fi

echo "Creating NEAR account..."
rm ~/.near-credentials/local/test.near.json || true
rm ~/.near-credentials/local/aurora.test.near.json || true
$WORKDIR/near-cli/bin/near create-account aurora.test.near \
    --master-account=test.near \
    --initial-balance 1000000 \
    --key-path $WORKDIR/nearData/validator_key.json \
    --node-url http://${NEARCORE_HOST}:3030

rm $WORKDIR/aurora.test.near.json || true
chmod 777 ~/.near-credentials/local/aurora.test.near.json
cp ~/.near-credentials/local/aurora.test.near.json $WORKDIR/aurora.test.near.json

echo "Downloading contract..."
rm $WORKDIR/contract.wasm || true
curl -L $CONTRACT_URL -o $WORKDIR/contract.wasm

echo "Installing contract..."
$WORKDIR/aurora-cli/lib/aurora.js install \
    --chain 1313161556 \
    --owner aurora.test.near \
    --signer aurora.test.near \
    --engine aurora.test.near \
    --endpoint http://${NEARCORE_HOST}:3030 \
    $WORKDIR/contract.wasm


text:
#!/bin/bash -e

RUNNER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $RUNNER_DIR/../controls/common.sh

rm -rf $WORKDIR || true

echo "Restoring workdir from cache..."
cache-util restore relayer-ci-workdir-${COMMIT_HASH}:${WORKDIR} || true

$RUNNER_DIR/../setup.sh --reset --start


text:
#!/bin/bash -e

RUNNER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $RUNNER_DIR/../controls/common.sh

$RUNNER_DIR/../stop.sh
rm -rf $WORKDIR || true


text:
#!/bin/bash -e

RUNNER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $RUNNER_DIR/../controls/common.sh

$RUNNER_DIR/../stop.sh --clean


text:
#!/bin/bash -e

RUNNER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
source $RUNNER_DIR/../controls/common.sh

rm -rf $WORKDIR || true
$RUNNER_DIR/../setup.sh

echo "Saving workdir to cache..."
rm -rf $WORKDIR/nearData $WORKDIR/near-cli $WORKDIR/aurora-cli $WORKDIR/contract.wasm || true
cache-util msave relayer-ci-workdir-${COMMIT_HASH}:${WORKDIR} || true
rm -rf $WORKDIR || true


text:
---
port: 8545
database: postgres://aurora:aurora@database/aurora
network: testnet
endpoint: https://archival-rpc.testnet.near.org
engine: develop.aurora
signer: relay.aurora
signerKey:
writable: true


text:
---
port: 8545
database: postgres://aurora:aurora@database/aurora
network: mainnet
endpoint: https://archival-rpc.mainnet.near.org
engine: aurora
signer: relay.aurora
signerKey:
writable: true


text:
---
port: 8545
database: postgres://aurora:aurora@database/aurora
network: testnet
endpoint: https://archival-rpc.testnet.near.org
engine: aurora
signer: relay.aurora
signerKey:
writable: true


text:
---
port: 8545
database: postgres://aurora:aurora@database/aurora
network: local
endpoint: http://host.docker.internal:3030
engine: aurora.test.near
signer: test.near
signerKey: ~/.near/validator_key.json
writable: true


text:
---
IPs:
EOAs:
CAs:


text:
---
port: 8545
database: postgres://aurora:aurora@database/aurora
network: betanet
endpoint: https://rpc.betanet.near.org
engine: aurora
signer: relay.aurora
signerKey:
writable: true


text:
---
port: 8545
database: postgres://aurora:aurora@database/aurora
broker:
network:
endpoint:
engine:
signer:
signerKey:


text:
---
web3_clientVersion:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#web3_clientVersion
  implementations:
    - src/servers/skeleton.ts

web3_sha3:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#web3_sha3
  implementations:
    - src/servers/skeleton.ts

net_listening:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#net_listening
  implementations:
    - src/servers/skeleton.ts

net_peerCount:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#net_peerCount
  implementations:
    - src/servers/skeleton.ts

net_version:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#net_version
  implementations:
    - src/servers/skeleton.ts

eth_accounts:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_accounts
  implementations:
    - src/servers/skeleton.ts

eth_blockNumber:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_blockNumber
  implementations:
    - etc/schema/functions/eth_blockNumber.sql
    - src/servers/skeleton.ts

eth_call:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_call
  implementations:
    - src/servers/database.ts
    - src/servers/skeleton.ts

eth_chainId:
  status: ok
  comment: EIP-695
  link: https://eips.ethereum.org/EIPS/eip-695
  implementations:
    - src/servers/skeleton.ts

eth_coinbase:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_coinbase
  implementations:
    - src/servers/skeleton.ts

eth_compileLLL:
  status: unsupported
  comment:
  link: https://eth.wiki/json-rpc/API#eth_compileLLL
  implementations:
    - src/servers/skeleton.ts

eth_compileSerpent:
  status: unsupported
  comment:
  link: https://eth.wiki/json-rpc/API#eth_compileSerpent
  implementations:
    - src/servers/skeleton.ts

eth_compileSolidity:
  status: unsupported
  comment:
  link: https://eth.wiki/json-rpc/API#eth_compileSolidity
  implementations:
    - src/servers/skeleton.ts

eth_estimateGas:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_estimateGas
  implementations:
    - src/servers/skeleton.ts

eth_gasPrice:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_gasPrice
  implementations:
    - src/servers/skeleton.ts

eth_getBalance:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getBalance
  implementations:
    - src/servers/skeleton.ts

eth_getBlockByHash:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getBlockByHash
  implementations:
    - etc/schema/functions/eth_getBlockByHash.sql
    - src/servers/skeleton.ts

eth_getBlockByNumber:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getBlockByNumber
  implementations:
    - etc/schema/functions/eth_getBlockByNumber.sql
    - src/servers/skeleton.ts

eth_getBlockTransactionCountByHash:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getBlockTransactionCountByHash
  implementations:
    - etc/schema/functions/eth_getBlockTransactionCountByHash.sql
    - src/servers/skeleton.ts

eth_getBlockTransactionCountByNumber:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getBlockTransactionCountByNumber
  implementations:
    - etc/schema/functions/eth_getBlockTransactionCountByNumber.sql
    - src/servers/skeleton.ts

eth_getCode:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getCode
  implementations:
    - src/servers/skeleton.ts

eth_getCompilers:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getCompilers
  implementations:
    - src/servers/skeleton.ts

eth_getFilterChanges:
  status: wip
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getFilterChanges
  implementations:
    - etc/schema/functions/eth_getFilterChanges.sql
    - src/servers/skeleton.ts

eth_getFilterLogs:
  status: wip
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getFilterLogs
  implementations:
    - etc/schema/functions/eth_getFilterLogs.sql
    - src/servers/skeleton.ts

eth_getLogs:
  status: wip
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getLogs
  implementations:
    - src/servers/skeleton.ts

eth_getProof:
  status: unsupported
  comment: EIP-1186
  link: https://eips.ethereum.org/EIPS/eip-1186
  implementations:
    - src/servers/skeleton.ts

eth_getStorageAt:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getStorageAt
  implementations:
    - src/servers/skeleton.ts

eth_getTransactionByBlockHashAndIndex:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getTransactionByBlockHashAndIndex
  implementations:
    - etc/schema/functions/eth_getTransactionByBlockHashAndIndex.sql
    - src/servers/skeleton.ts

eth_getTransactionByBlockNumberAndIndex:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getTransactionByBlockNumberAndIndex
  implementations:
    - etc/schema/functions/eth_getTransactionByBlockNumberAndIndex.sql
    - src/servers/skeleton.ts

eth_getTransactionByHash:
  status: wip
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getTransactionByHash
  implementations:
    - etc/schema/functions/eth_getTransactionByHash.sql
    - src/servers/skeleton.ts

eth_getTransactionCount:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getTransactionCount
  implementations:
    - etc/schema/functions/eth_getTransactionCount.sql
    - src/servers/skeleton.ts

eth_getTransactionReceipt:
  status: wip
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getTransactionReceipt
  implementations:
    - src/servers/skeleton.ts

eth_getUncleByBlockHashAndIndex:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getUncleByBlockHashAndIndex
  implementations:
    - etc/schema/functions/eth_getUncleByBlockHashAndIndex.sql
    - src/servers/skeleton.ts

eth_getUncleByBlockNumberAndIndex:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getUncleByBlockNumberAndIndex
  implementations:
    - etc/schema/functions/eth_getUncleByBlockNumberAndIndex.sql
    - src/servers/skeleton.ts

eth_getUncleCountByBlockHash:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getUncleCountByBlockHash
  implementations:
    - etc/schema/functions/eth_getUncleCountByBlockHash.sql
    - src/servers/skeleton.ts

eth_getUncleCountByBlockNumber:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getUncleCountByBlockNumber
  implementations:
    - etc/schema/functions/eth_getUncleCountByBlockNumber.sql
    - src/servers/skeleton.ts

eth_getWork:
  status: unsupported
  comment:
  link: https://eth.wiki/json-rpc/API#eth_getWork
  implementations:
    - src/servers/skeleton.ts

eth_hashrate:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_hashrate
  implementations:
    - src/servers/skeleton.ts

eth_mining:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_mining
  implementations:
    - src/servers/skeleton.ts

eth_newBlockFilter:
  status: wip
  comment:
  link: https://eth.wiki/json-rpc/API#eth_newBlockFilter
  implementations:
    - etc/schema/functions/eth_newBlockFilter.sql
    - src/servers/skeleton.ts

eth_newFilter:
  status: wip
  comment:
  link: https://eth.wiki/json-rpc/API#eth_newFilter
  implementations:
    - etc/schema/functions/eth_newFilter.sql
    - src/servers/skeleton.ts

eth_newPendingTransactionFilter:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_newPendingTransactionFilter
  implementations:
    - etc/schema/functions/eth_newPendingTransactionFilter.sql
    - src/servers/skeleton.ts

eth_pendingTransactions:
  status: ok
  comment: '[Undocumented](https://github.com/ethereum/go-ethereum/issues/'
  link: https://github.com/ethereum/wiki/issues/685
  implementations:
    - src/servers/skeleton.ts

eth_protocolVersion:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_protocolVersion
  implementations:
    - src/servers/skeleton.ts

eth_sendRawTransaction:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_sendRawTransaction
  implementations:
    - src/servers/skeleton.ts

eth_sendTransaction:
  status: unsupported
  comment:
  link: https://eth.wiki/json-rpc/API#eth_sendTransaction
  implementations:
    - src/servers/skeleton.ts

eth_sign:
  status: unsupported
  comment:
  link: https://eth.wiki/json-rpc/API#eth_sign
  implementations:
    - src/servers/skeleton.ts

eth_signTransaction:
  status: unsupported
  comment:
  link: https://eth.wiki/json-rpc/API#eth_signTransaction
  implementations:
    - src/servers/skeleton.ts

eth_signTypedData:
  status: unsupported
  comment: EIP-712
  link: https://eips.ethereum.org/EIPS/eip-712
  implementations:
    - src/servers/skeleton.ts

eth_submitHashrate:
  status: unsupported
  comment:
  link: https://eth.wiki/json-rpc/API#eth_submitHashrate
  implementations:
    - src/servers/skeleton.ts

eth_submitWork:
  status: unsupported
  comment:
  link: https://eth.wiki/json-rpc/API#eth_submitWork
  implementations:
    - src/servers/skeleton.ts

eth_syncing:
  status: ok
  comment:
  link: https://eth.wiki/json-rpc/API#eth_syncing
  implementations:
    - src/servers/skeleton.ts

eth_uninstallFilter:
  status: wip
  comment:
  link: https://eth.wiki/json-rpc/API#eth_uninstallFilter
  implementations:
    - etc/schema/functions/eth_uninstallFilter.sql
    - src/servers/skeleton.ts

txpool_content:
  status: ok
  comment: Geth extension
  link: https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content
  implementations:
    - src/servers/skeleton.ts

txpool_inspect:
  status: ok
  comment: Geth extension
  link: https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_inspect
  implementations:
    - src/servers/skeleton.ts

txpool_status:
  status: ok
  comment: Geth extension
  link: https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_status
  implementations:
    - src/servers/skeleton.ts

parity_pendingTransactions:
  status: ok
  comment: Parity extension
  link: https://openethereum.github.io/JSONRPC-parity-module#parity_pendingtransactions
  implementations:
    - src/servers/skeleton.ts


text:
\i etc/schema/domains.sql
\i etc/schema/types/block_result.sql
\i etc/schema/types/filter_result.sql
\i etc/schema/types/filter_type.sql
\i etc/schema/types/transaction_receipt.sql
\i etc/schema/types/transaction_result.sql
\i etc/schema/tables/block.sql
\i etc/schema/tables/filter.sql
\i etc/schema/tables/subscription.sql
\i etc/schema/tables/transaction.sql
\i etc/schema/tables/event.sql
\i etc/schema/functions/eth_blockNumber.sql
\i etc/schema/functions/eth_getBlockByHash.sql
\i etc/schema/functions/eth_getBlockByNumber.sql
\i etc/schema/functions/eth_getBlockTransactionCountByHash.sql
\i etc/schema/functions/eth_getBlockTransactionCountByNumber.sql
\i etc/schema/functions/eth_getFilterChanges.sql
\i etc/schema/functions/eth_getFilterLogs.sql
\i etc/schema/functions/eth_getTransactionByBlockHashAndIndex.sql
\i etc/schema/functions/eth_getTransactionByBlockNumberAndIndex.sql
\i etc/schema/functions/eth_getTransactionByHash.sql
\i etc/schema/functions/eth_getTransactionCount.sql
\i etc/schema/functions/eth_getTransactionReceipt.sql
\i etc/schema/functions/eth_getUncleByBlockHashAndIndex.sql
\i etc/schema/functions/eth_getUncleByBlockNumberAndIndex.sql
\i etc/schema/functions/eth_getUncleCountByBlockHash.sql
\i etc/schema/functions/eth_getUncleCountByBlockNumber.sql
\i etc/schema/functions/eth_newBlockFilter.sql
\i etc/schema/functions/eth_newFilter.sql
\i etc/schema/functions/eth_newPendingTransactionFilter.sql
\i etc/schema/functions/eth_uninstallFilter.sql


text:
DROP DOMAIN IF EXISTS address CASCADE;
CREATE DOMAIN address AS bytea
  CHECK (length(value) = 20);

DROP DOMAIN IF EXISTS blockno CASCADE;
CREATE DOMAIN blockno AS int8
  CHECK (value >= 0);

DROP DOMAIN IF EXISTS chainid CASCADE;
CREATE DOMAIN chainid AS int4
  CHECK (value >= 1);

DROP DOMAIN IF EXISTS hash CASCADE;
CREATE DOMAIN hash AS bytea
  CHECK (length(value) = 32);

DROP DOMAIN IF EXISTS instant CASCADE;
CREATE DOMAIN instant AS timestamptz
  CHECK (value = timestamptz '1970-01-01T00:00:00Z' OR value > timestamptz '2015-07-30T00:00:00Z');

DROP DOMAIN IF EXISTS u64 CASCADE;
CREATE DOMAIN u64 AS numeric(20, 0)
  CHECK (value >= 0 AND value <= 18446744073709551615);

DROP DOMAIN IF EXISTS u256 CASCADE;
CREATE DOMAIN u256 AS numeric(78, 0)
  CHECK (value >= 0 AND value <= 115792089237316195423570985008687907853269984665640564039457584007913129639935);


text:
DROP TYPE IF EXISTS transaction_receipt CASCADE;

CREATE TYPE transaction_receipt AS (
    "blockNumber" blockno,
    "blockHash" hash,
    "transactionIndex" int,
    "transactionHash" hash,
    "from" address,
    "to" address,
    "gasUsed" u256,
    "cumulativeGasUsed" u256,
    "contractAddress" address,
    "logs" bigint[],
    "logsBloom" bytea, -- 256 bytes
    "status" smallint,
    "nearTransactionHash" hash,
    "nearReceiptHash" hash
);


text:
DROP TYPE IF EXISTS filter_result CASCADE;

CREATE TYPE filter_result AS (
    "blockNumber" blockno,
    "blockHash" hash,
    "transactionIndex" int,
    "transactionHash" hash,
    "logIndex" int,
    "address" address,
    "topics" hash[4],
    "data" bytea,
    "removed" boolean
);


text:
DROP TYPE IF EXISTS block_result CASCADE;

CREATE TYPE block_result AS (
    "number" blockno,
    "hash" hash,
    "parentHash" hash,
    "nonce" bytea,           -- 8 bytes
    "sha3Uncles" hash,
    "logsBloom" bytea,       -- 256 bytes
    "transactionsRoot" hash,
    "stateRoot" hash,
    "receiptsRoot" hash,
    "miner" address,
    "difficulty" int2,
    "totalDifficulty" int2,
    "extraData" bytea,       -- 0 bytes
    "size" int4,
    "gasLimit" int8,
    "gasUsed" int8,
    "timestamp" int4,
    "mixHash" hash
);


text:
DROP TYPE IF EXISTS transaction_result CASCADE;

CREATE TYPE transaction_result AS (
    "blockNumber" blockno,
    "blockHash" hash,
    "transactionIndex" int,
    "hash" hash,
    "from" address,
    "to" address,
    "gas" u256,
    "gasPrice" u256,
    "nonce" u256,
    "value" u256,
    "input" bytea,
    "v" u64,
    "r" u256,
    "s" u256
);


text:
DROP TYPE IF EXISTS filter_type CASCADE;

CREATE TYPE filter_type AS ENUM ('block', 'event', 'transaction');


text:
DROP FUNCTION IF EXISTS eth_getFilterChanges_block(bytea) RESTRICT;
DROP FUNCTION IF EXISTS eth_getFilterChanges_event(bytea) RESTRICT;
DROP FUNCTION IF EXISTS eth_getFilterChanges_transaction(bytea) RESTRICT;

CREATE FUNCTION eth_getFilterChanges_block(filter_id bytea) RETURNS SETOF hash AS $$
DECLARE
  block_id blockno;
BEGIN
  SELECT coalesce(max(id), 0) FROM block INTO STRICT block_id;
  RETURN QUERY EXECUTE format('
    SELECT b.hash FROM block b
      WHERE b.id >= (SELECT coalesce(f.poll_block, f.from_block, 0) FROM filter f WHERE uuid_send(f.id) = %L)
        AND b.id <= (SELECT least(f.to_block, %L) FROM filter f WHERE uuid_send(f.id) = %L)
      ORDER BY b.id ASC;
  ', filter_id, block_id, filter_id);
  UPDATE filter SET poll_block = block_id + 1 WHERE uuid_send(id) = filter_id;
  RETURN;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;

CREATE FUNCTION eth_getFilterChanges_transaction(filter_id bytea) RETURNS SETOF hash AS $$
BEGIN
  RETURN; -- no pending transactions
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getTransactionByBlockHashAndIndex(hash, int) RESTRICT;

CREATE FUNCTION eth_getTransactionByBlockHashAndIndex(block_hash hash, transaction_index int) RETURNS transaction_result AS $$
DECLARE
  result transaction_result;
BEGIN
  SELECT
      b.id AS "blockNumber",
      b.hash AS "blockHash",
      t.index AS "transactionIndex",
      t.hash AS "hash",
      t.from AS "from",
      t.to AS "to",
      t.gas_limit AS "gas",
      t.gas_price AS "gasPrice",
      t.nonce AS "nonce",
      t.value AS "value",
      coalesce(t.input, '\x'::bytea) AS "input",
      t.v AS "v",
      t.r AS "r",
      t.s AS "s"
    FROM transaction t
      LEFT JOIN block b ON t.block = b.id
    WHERE b.hash = block_hash AND t.index = transaction_index
    LIMIT 1
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getUncleByBlockHashAndIndex(hash, int) RESTRICT;

CREATE FUNCTION eth_getUncleByBlockHashAndIndex(block_hash hash, uncle_index int) RETURNS block_result AS $$
BEGIN
  RETURN NULL; -- no uncle blocks
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getTransactionReceipt(hash) RESTRICT;

CREATE FUNCTION eth_getTransactionReceipt(transaction_hash hash) RETURNS transaction_receipt AS $$
DECLARE
  result transaction_receipt;
BEGIN
  SELECT
      b.id AS "blockNumber",
      b.hash AS "blockHash",
      t.index AS "transactionIndex",
      t.hash AS "transactionHash",
      t.from AS "from",
      t.to AS "to",
      t.gas_used AS "gasUsed",
      0::u256 AS "cumulativeGasUsed", -- TODO: tally?
      CASE WHEN (t.to IS NULL OR t.to = '\x0000000000000000000000000000000000000000') AND length(t.output) = 20 THEN t.output
           ELSE NULL
      END AS "contractAddress",
      NULL AS "logs",                 -- TODO: fetch event.id[]
      repeat('\000', 256)::bytea AS "logsBloom",
      CASE WHEN t.status THEN 1 ELSE 0 END AS "status",
      t.near_hash AS "nearTransactionHash",
      t.near_receipt_hash AS "nearReceiptHash"
    FROM transaction t
      LEFT JOIN block b ON t.block = b.id
    WHERE t.hash = transaction_hash
    LIMIT 1
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getFilterLogs_block(bytea) RESTRICT;
DROP FUNCTION IF EXISTS eth_getFilterLogs_event(bytea) RESTRICT;
DROP FUNCTION IF EXISTS eth_getFilterLogs_transaction(bytea) RESTRICT;

CREATE FUNCTION eth_getFilterLogs_block(filter_id bytea) RETURNS SETOF hash AS $$
DECLARE
  block_id blockno;
BEGIN
  SELECT coalesce(max(id), 0) FROM block INTO STRICT block_id;
  RETURN QUERY EXECUTE format('
    SELECT b.hash FROM block b
      WHERE b.id >= (SELECT coalesce(f.from_block, 0) FROM filter f WHERE uuid_send(f.id) = %L)
        AND b.id <= (SELECT least(f.to_block, %L) FROM filter f WHERE uuid_send(f.id) = %L)
      ORDER BY b.id ASC;
  ', filter_id, block_id, filter_id);
  RETURN;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;

CREATE FUNCTION eth_getFilterLogs_transaction(filter_id bytea) RETURNS SETOF hash AS $$
BEGIN
  RETURN; -- no pending transactions
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getTransactionByHash(hash) RESTRICT;

CREATE FUNCTION eth_getTransactionByHash(transaction_hash hash) RETURNS transaction_result AS $$
DECLARE
  result transaction_result;
BEGIN
  SELECT
      b.id AS "blockNumber",
      b.hash AS "blockHash",
      t.index AS "transactionIndex",
      t.hash AS "hash",
      t.from AS "from",
      t.to AS "to",
      t.gas_limit AS "gas",
      t.gas_price AS "gasPrice",
      t.nonce AS "nonce",
      t.value AS "value",
      coalesce(t.input, '\x'::bytea) AS "input",
      t.v AS "v",
      t.r AS "r",
      t.s AS "s"
    FROM transaction t
      LEFT JOIN block b ON t.block = b.id
    WHERE t.hash = transaction_hash
    LIMIT 1
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getUncleByBlockNumberAndIndex(blockno, int) RESTRICT;

CREATE FUNCTION eth_getUncleByBlockNumberAndIndex(block_id blockno, uncle_index int) RETURNS block_result AS $$
BEGIN
  RETURN NULL; -- no uncle blocks
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getUncleCountByBlockHash(hash) RESTRICT;

CREATE FUNCTION eth_getUncleCountByBlockHash(block_hash hash) RETURNS bigint AS $$
BEGIN
  PERFORM id FROM block WHERE hash = block_hash LIMIT 1;
  IF FOUND THEN RETURN 0; ELSE RETURN NULL; END IF;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_newPendingTransactionFilter() RESTRICT;

CREATE FUNCTION eth_newPendingTransactionFilter() RETURNS bytea AS $$
BEGIN
  RETURN repeat('\000', 16)::bytea; -- the empty filter
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getBlockTransactionCountByHash(hash) RESTRICT;

CREATE FUNCTION eth_getBlockTransactionCountByHash(block_hash hash) RETURNS bigint AS $$
DECLARE
  result bigint;
BEGIN
  -- FIXME: return NULL for unknown blocks
  SELECT COUNT(t.id)
    FROM transaction t
      LEFT JOIN block b ON t.block = b.id
    WHERE b.hash = block_hash
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_newBlockFilter(inet) RESTRICT;

CREATE FUNCTION eth_newBlockFilter(client_ip inet) RETURNS bytea AS $$
DECLARE
  block_id blockno;
  filter_uuid uuid;
BEGIN
  SELECT max(id) FROM block INTO STRICT block_id;
  INSERT INTO filter
      (id, type, created_at, created_by, poll_block)
    VALUES
      (gen_random_uuid(), 'block', now(), client_ip, block_id + 1)
    RETURNING id INTO STRICT filter_uuid;
  RETURN uuid_send(filter_uuid);
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getTransactionByBlockNumberAndIndex(blockno, int) RESTRICT;

CREATE FUNCTION eth_getTransactionByBlockNumberAndIndex(block_id blockno, transaction_index int) RETURNS transaction_result AS $$
DECLARE
  result transaction_result;
BEGIN
  SELECT
      b.id AS "blockNumber",
      b.hash AS "blockHash",
      t.index AS "transactionIndex",
      t.hash AS "hash",
      t.from AS "from",
      t.to AS "to",
      t.gas_limit AS "gas",
      t.gas_price AS "gasPrice",
      t.nonce AS "nonce",
      t.value AS "value",
      coalesce(t.input, '\x'::bytea) AS "input",
      t.v AS "v",
      t.r AS "r",
      t.s AS "s"
    FROM transaction t
      LEFT JOIN block b ON t.block = b.id
    WHERE b.id = block_id AND t.index = transaction_index
    LIMIT 1
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getBlockTransactionCountByNumber(blockno) RESTRICT;

CREATE FUNCTION eth_getBlockTransactionCountByNumber(block_id blockno) RETURNS bigint AS $$
DECLARE
  result bigint;
BEGIN
  -- FIXME: return NULL for unknown blocks
  SELECT COUNT(id)
    FROM transaction
    WHERE block = block_id
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getTransactionCount(address, blockno) RESTRICT;

CREATE FUNCTION eth_getTransactionCount(address address, block_id blockno) RETURNS bigint AS $$
DECLARE
  result bigint;
BEGIN -- TODO: use nonce column?
  SELECT COUNT(id) FROM transaction
    WHERE "from" = address AND block <= block_id
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_uninstallFilter(inet, bigint) RESTRICT;

CREATE FUNCTION eth_uninstallFilter(client_ip inet, filter_id bytea) RETURNS boolean AS $$
BEGIN
  DELETE FROM filter
    WHERE uuid_send(id) = filter_id AND created_by = client_ip;
  RETURN FOUND;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_blockNumber() RESTRICT;

CREATE FUNCTION eth_blockNumber() RETURNS blockno AS $$
DECLARE
  block_id blockno;
BEGIN
  SELECT COALESCE(MAX(id), 0) FROM block INTO STRICT block_id;
  RETURN block_id;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getBlockByHash(hash) RESTRICT;

CREATE FUNCTION eth_getBlockByHash(block_hash hash) RETURNS block_result AS $$
DECLARE
  result block_result;
  block_id blockno;
BEGIN
  SELECT id FROM block WHERE hash = block_hash INTO STRICT block_id;
  SELECT * FROM eth_getBlockByNumber(block_id) INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getBlockByNumber(blockno) RESTRICT;

CREATE FUNCTION eth_getBlockByNumber(block_id blockno) RETURNS block_result AS $$
DECLARE
  result block_result;
BEGIN
  SELECT
      id,                           -- number
      hash,                         -- hash
      parent_hash,                  -- parentHash
      repeat('\000', 8)::bytea,     -- nonce
      '\x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',    -- sha3Uncles keccak256(rlp.encode([]))
      repeat('\000', 256)::bytea,   -- logsBloom
      transactions_root,            -- transactionsRoot
      state_root,                   -- stateRoot
      receipts_root,                -- receiptsRoot
      repeat('\000', 20)::bytea,    -- miner
      0,                            -- difficulty
      0,                            -- totalDifficulty
      ''::bytea,                    -- extraData
      size,                         -- size
      gas_limit,                    -- gasLimit
      gas_used,                     -- gasUsed
      COALESCE(EXTRACT(EPOCH FROM timestamp), 0)::int4, -- timestamp
      repeat('\000', 32)::hash      -- mixHash
    FROM block
    WHERE id = block_id
    LIMIT 1
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_getUncleCountByBlockNumber(blockno) RESTRICT;

CREATE FUNCTION eth_getUncleCountByBlockNumber(block_id blockno) RETURNS bigint AS $$
BEGIN
  PERFORM id FROM block WHERE id = block_id LIMIT 1;
  IF FOUND THEN RETURN 0; ELSE RETURN NULL; END IF;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP FUNCTION IF EXISTS eth_newFilter(inet, blockno, blockno, address[], jsonb) RESTRICT;

CREATE FUNCTION eth_newFilter(client_ip inet, from_block blockno, to_block blockno, addresses address[], topics jsonb) RETURNS bytea AS $$
DECLARE
  block_id blockno;
  filter_uuid uuid;
BEGIN
  SELECT max(id) FROM block INTO STRICT block_id;
  INSERT INTO filter
      (id, type, created_at, created_by, poll_block, from_block, to_block, addresses, topics)
    VALUES
      (gen_random_uuid(), 'event', now(), client_ip, block_id + 1, from_block, to_block, addresses, topics)
    RETURNING id INTO STRICT filter_uuid;
  RETURN uuid_send(filter_uuid);
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;


text:
DROP TABLE IF EXISTS block CASCADE;

CREATE TABLE block (
  chain             chainid NOT NULL,
  id                blockno NOT NULL PRIMARY KEY,
  hash              hash NOT NULL UNIQUE,
  near_hash         hash NULL UNIQUE,
  timestamp         instant NULL, -- NULL for unknown (pre-HF)
  size              int4 NOT NULL,
  gas_limit         u256 NOT NULL,
  gas_used          u256 NOT NULL CHECK (gas_used <= gas_limit),
  parent_hash       hash NOT NULL UNIQUE,
  transactions_root hash NOT NULL,
  state_root        hash NOT NULL,
  receipts_root     hash NOT NULL
);

CREATE UNIQUE INDEX block_chain_id_idx ON block (chain, id);

CREATE INDEX block_timestamp_idx ON block USING btree (timestamp);


text:
DROP TABLE IF EXISTS transaction CASCADE;

CREATE TABLE transaction (
  block             blockno NOT NULL REFERENCES block ON DELETE CASCADE,
  index             int NOT NULL CHECK (index >= 0),
  id                bigserial NOT NULL PRIMARY KEY,
  hash              hash NOT NULL UNIQUE,
  near_hash         hash NOT NULL,
  near_receipt_hash hash NULL,
  "from"            address NOT NULL,
  "to"              address NULL,
  nonce             u256 NOT NULL,
  gas_price         u256 NOT NULL,
  gas_limit         u256 NOT NULL,
  gas_used          u256 NOT NULL, -- FIXME: CHECK (gas_used <= gas_limit),
  value             u256 NOT NULL,
  input             bytea NULL CHECK (length(input) > 0),
  v                 u64 NULL,
  r                 u256 NULL,
  s                 u256 NULL,
  status            boolean NOT NULL,
  output            bytea NULL CHECK (length(output) > 0)
);

CREATE UNIQUE INDEX transaction_block_index_idx ON transaction (block, index);

CREATE INDEX transaction_block_idx ON transaction USING btree (block);

CREATE INDEX transaction_from_idx ON transaction USING btree ("from");

CREATE INDEX transaction_to_idx ON transaction USING btree ("to");


text:
DROP TABLE IF EXISTS event CASCADE;

CREATE TABLE event (
  transaction       bigint NOT NULL REFERENCES transaction ON DELETE CASCADE,
  index             int NOT NULL CHECK (index >= 0),
  id                bigserial NOT NULL PRIMARY KEY,
  data              bytea NULL CHECK (length(data) > 0),
  "from"            address NULL,
  topics            hash[] NULL CHECK (array_length(topics, 1) > 0 AND array_length(topics, 1) <= 4)
);

CREATE UNIQUE INDEX event_transaction_index_idx ON event (transaction, index);


text:
DROP TABLE IF EXISTS subscription CASCADE;

CREATE TABLE subscription (
	sec_websocket_key varchar COLLATE "default",
	id                varchar COLLATE "default",
	type              varchar COLLATE "default",
	ip                varchar COLLATE "default",
	filter            jsonb NULL
);

CREATE UNIQUE INDEX subscription_sec_websocket_key_type_filter_index_idx ON subscription (sec_websocket_key, type, filter);


text:
DROP TABLE IF EXISTS filter CASCADE;

CREATE TABLE filter (
  id                uuid NOT NULL PRIMARY KEY,
  type              filter_type NOT NULL,
  created_at        instant NOT NULL,
  created_by        inet NOT NULL,
  polled_at         instant NULL,
  poll_block        blockno NULL,
  from_block        blockno NULL,
  to_block          blockno NULL,
  addresses         address[] NULL CHECK (array_length(addresses, 1) > 0),
  topics            jsonb NULL
);


text: UPDATE event SET "from" = repeat('\000', 20)::bytea WHERE transaction = 51 AND "from" IS NULL;


text:
-- +goose Up
-- +goose StatementBegin
--Make sure it will not fail on db
ALTER TYPE block_result DROP ATTRIBUTE IF EXISTS "mixHash"; --Make sure it will not fail on db
ALTER TYPE block_result ADD ATTRIBUTE "mixHash" hash;
-- Move mixHash to the end, since order is important
CREATE OR REPLACE FUNCTION eth_getBlockByNumber(block_id blockno) RETURNS block_result AS $$
DECLARE
  result block_result;
BEGIN
  SELECT
      id,                           -- number
      hash,                         -- hash
      parent_hash,                  -- parentHash
      repeat('\000', 8)::bytea,     -- nonce
      '\x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',    -- sha3Uncles keccak256(rlp.encode([]))
      repeat('\000', 256)::bytea,   -- logsBloom
      transactions_root,            -- transactionsRoot
      state_root,                   -- stateRoot
      receipts_root,                -- receiptsRoot
      repeat('\000', 20)::bytea,    -- miner
      0,                            -- difficulty
      0,                            -- totalDifficulty
      ''::bytea,                    -- extraData
      size,                         -- size
      gas_limit,                    -- gasLimit
      gas_used,                     -- gasUsed
      COALESCE(EXTRACT(EPOCH FROM timestamp), 0)::int4, -- timestamp
      repeat('\000', 32)::hash      -- mixHash
    FROM block
    WHERE id = block_id
    LIMIT 1
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
ALTER TYPE block_result DROP ATTRIBUTE IF EXISTS "mixHash";
CREATE OR REPLACE FUNCTION eth_getBlockByNumber(block_id blockno) RETURNS block_result AS $$
DECLARE
  result block_result;
BEGIN
  SELECT
      id,                           -- number
      hash,                         -- hash
      parent_hash,                  -- parentHash
      repeat('\000', 8)::bytea,     -- nonce
      '\x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',    -- sha3Uncles keccak256(rlp.encode([]))
      repeat('\000', 256)::bytea,   -- logsBloom
      transactions_root,            -- transactionsRoot
      state_root,                   -- stateRoot
      receipts_root,                -- receiptsRoot
      repeat('\000', 20)::bytea,    -- miner
      0,                            -- difficulty
      0,                            -- totalDifficulty
      ''::bytea,                    -- extraData
      size,                         -- size
      gas_limit,                    -- gasLimit
      gas_used,                     -- gasUsed
      COALESCE(EXTRACT(EPOCH FROM timestamp), 0)::int4 -- timestamp
    FROM block
    WHERE id = block_id
    LIMIT 1
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
-- +goose StatementEnd


text:
-- +goose Up
-- +goose StatementBegin
CREATE OR REPLACE FUNCTION eth_getBlockByNumber(block_id blockno) RETURNS block_result AS $$
DECLARE
  result block_result;
BEGIN
  SELECT
      id,                           -- number
      hash,                         -- hash
      parent_hash,                  -- parentHash
      repeat('\000', 8)::bytea,     -- nonce
      '\x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',    -- sha3Uncles keccak256(rlp.encode([]))
      repeat('\000', 256)::bytea,   -- logsBloom
      transactions_root,            -- transactionsRoot
      state_root,                   -- stateRoot
      receipts_root,                -- receiptsRoot
      repeat('\000', 20)::bytea,    -- miner
      0,                            -- difficulty
      0,                            -- totalDifficulty
      ''::bytea,                    -- extraData
      size,                         -- size
      gas_limit,                    -- gasLimit
      gas_used,                     -- gasUsed
      repeat('\000', 32)::hash,     -- mixHash
      COALESCE(EXTRACT(EPOCH FROM timestamp), 0)::int4 -- timestamp
    FROM block
    WHERE id = block_id
    LIMIT 1
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
CREATE OR REPLACE FUNCTION eth_getBlockByNumber(block_id blockno) RETURNS block_result AS $$
DECLARE
  result block_result;
BEGIN
  SELECT
      id,                           -- number
      hash,                         -- hash
      parent_hash,                  -- parentHash
      repeat('\000', 8)::bytea,     -- nonce
      repeat('\000', 32)::bytea,    -- sha3Uncles
      repeat('\000', 256)::bytea,   -- logsBloom
      transactions_root,            -- transactionsRoot
      state_root,                   -- stateRoot
      receipts_root,                -- receiptsRoot
      repeat('\000', 20)::bytea,    -- miner
      0,                            -- difficulty
      0,                            -- totalDifficulty
      ''::bytea,                    -- extraData
      size,                         -- size
      gas_limit,                    -- gasLimit
      gas_used,                     -- gasUsed
      repeat('\000', 32)::hash,     -- mixHash
      COALESCE(EXTRACT(EPOCH FROM timestamp), 0)::int4 -- timestamp
    FROM block
    WHERE id = block_id
    LIMIT 1
    INTO STRICT result;
  RETURN result;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
-- +goose StatementEnd


text:
-- +goose Up
ALTER TABLE event ALTER COLUMN "from" SET NOT NULL;
-- +goose StatementBegin
SELECT 'up SQL query';
-- +goose StatementEnd

-- +goose Down
ALTER TABLE event ALTER COLUMN "from" DROP NOT NULL;
-- +goose StatementBegin
SELECT 'down SQL query';
-- +goose StatementEnd


text:
-- +goose Up
ALTER TABLE event ADD COLUMN IF NOT EXISTS "from" address;
-- +goose StatementBegin
SELECT 'up SQL query';
-- +goose StatementEnd

-- +goose Down
ALTER TABLE event DROP COLUMN "from";
-- +goose StatementBegin
SELECT 'down SQL query';
-- +goose StatementEnd


text:
-- +goose Up
-- +goose StatementBegin
DROP FUNCTION IF EXISTS eth_getFilterChanges_event(bytea) RESTRICT;
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP FUNCTION IF EXISTS eth_getFilterChanges_event(bytea) RESTRICT;
CREATE FUNCTION eth_getFilterChanges_event(filter_id bytea) RETURNS SETOF filter_result AS $$
DECLARE
  block_id blockno;
BEGIN
  SELECT coalesce(max(id), 0) FROM block INTO STRICT block_id;
  RETURN QUERY EXECUTE format(E'
    SELECT
        b.id AS "blockNumber",
        b.hash AS "blockHash",
        t.index AS "transactionIndex",
        t.hash AS "transactionHash",
        e.index AS "logIndex",
        e.from AS "address",
        e.topics AS "topics",
        coalesce(e.data, repeat(\'\\000\', 32)::bytea) AS "data",
        false AS "removed"
      FROM event e
        LEFT JOIN transaction t ON e.transaction = t.id
        LEFT JOIN block b ON t.block = b.id
      WHERE b.id >= (SELECT coalesce(f.poll_block, f.from_block, 0) FROM filter f WHERE uuid_send(f.id) = %L)
        AND b.id <= (SELECT least(f.to_block, %L) FROM filter f WHERE uuid_send(f.id) = %L)
      ORDER BY b.id ASC;
  ', filter_id, block_id, filter_id);
  UPDATE filter SET poll_block = block_id + 1 WHERE uuid_send(id) = filter_id;
  RETURN;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
-- +goose StatementEnd


text:
-- +goose Up
-- +goose StatementBegin
DROP FUNCTION IF EXISTS eth_getFilterLogs_event(bytea) RESTRICT;
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
DROP FUNCTION IF EXISTS eth_getFilterLogs_event(bytea) RESTRICT;
CREATE FUNCTION eth_getFilterLogs_event(filter_id bytea) RETURNS SETOF filter_result AS $$
DECLARE
  block_id blockno;
BEGIN
  SELECT coalesce(max(id), 0) FROM block INTO STRICT block_id;
  RETURN QUERY EXECUTE format(E'
    SELECT
        b.id AS "blockNumber",
        b.hash AS "blockHash",
        t.index AS "transactionIndex",
        t.hash AS "transactionHash",
        e.index AS "logIndex",
        e.from AS "address",
        e.topics AS "topics",
        coalesce(e.data, repeat(\'\\000\', 32)::bytea) AS "data",
        false AS "removed"
      FROM event e
        LEFT JOIN transaction t ON e.transaction = t.id
        LEFT JOIN block b ON t.block = b.id
      WHERE b.id >= (SELECT coalesce(f.from_block, 0) FROM filter f WHERE uuid_send(f.id) = %L)
        AND b.id <= (SELECT least(f.to_block, %L) FROM filter f WHERE uuid_send(f.id) = %L)
      ORDER BY b.id ASC;
  ', filter_id, block_id, filter_id);
  RETURN;
END;
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
-- +goose StatementEnd


text:
# WindowScrape
Used in personal scripting for manipulating external applications and their windows.

# License

The Unlicense / Public Domain
[https://unlicense.org/]

repo_name: DataDink/WindowScrape

repo_language: C#


text:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
  <PropertyGroup>
    <ProjectType>local</ProjectType>
    <ProjectVersion>7.10.377</ProjectVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{9EE32BA3-7B86-44B3-9F85-9EDB0EACF710}</ProjectGuid>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <AssemblyName>WindowScrape</AssemblyName>
    <OutputType>Library</OutputType>
    <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
    <PlatformTarget>x86</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <PlatformTarget>x86</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
  <ItemGroup>
    <Reference Include="System.Drawing" />
    <Compile Include="AssemblyInfo.cs" />
    <Compile Include="Global.cs" />
    <Compile Include="WindowScrape\Constants\WM.cs" />
    <Compile Include="WindowScrape\Constants\PositioningFlags.cs" />
    <Compile Include="WindowScrape\Types\RECT.cs" />
    <Compile Include="WindowScrape\Static\HwndInterface.cs" />
    <Compile Include="WindowScrape\Types\HwndObject.cs" />
  </ItemGroup>
</Project>


text:
// Assembly WindowScrape, Version 1.0.0.0

[assembly: System.Reflection.AssemblyVersion("1.0.0.0")]
[assembly: System.Reflection.AssemblyCompany("DataDink")]
[assembly: System.Runtime.CompilerServices.RuntimeCompatibility(WrapNonExceptionThrows=true)]
[assembly: System.Reflection.AssemblyProduct("WindowScrape")]
[assembly: System.Reflection.AssemblyCopyright("Copyright \x00a9 DataDink 2009")]
[assembly: System.Diagnostics.Debuggable(System.Diagnostics.DebuggableAttribute.DebuggingModes.DisableOptimizations | System.Diagnostics.DebuggableAttribute.DebuggingModes.EnableEditAndContinue | System.Diagnostics.DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | System.Diagnostics.DebuggableAttribute.DebuggingModes.Default)]
[assembly: System.Reflection.AssemblyTitle("WindowScrape")]
[assembly: System.Reflection.AssemblyDescription("Simplifies manipulating Hwnd items.")]
[assembly: System.Reflection.AssemblyConfiguration("")]
[assembly: System.Runtime.CompilerServices.CompilationRelaxations(8)]
[assembly: System.Reflection.AssemblyTrademark("")]
[assembly: System.Runtime.InteropServices.ComVisible(false)]
[assembly: System.Runtime.InteropServices.Guid("beba9058-7a17-4079-81d8-f76f22da5260")]
[assembly: System.Reflection.AssemblyFileVersion("1.0.0.0")]


text:
namespace WindowScrape.Types
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Runtime.CompilerServices;
    using WindowScrape.Constants;
    using WindowScrape.Static;

    public class HwndObject
    {
        public HwndObject(IntPtr hwnd)
        {
            this.Hwnd = hwnd;
        }

        public void Click()
        {
            HwndInterface.ClickHwnd(this.Hwnd);
        }

        // <summary>
        // Bring this window to the foreground
        // </summary>
        public bool Activate()
        {
            return HwndInterface.ActivateWindow(Hwnd);
        }

        // <summary>
        // Minimize this window
        // </summary>
        public bool Minimize()
        {
            return HwndInterface.MinimizeWindow(Hwnd);
        }

        public override bool Equals(object obj)
        {
            if (object.ReferenceEquals(null, obj))
            {
                return false;
            }
            if (object.ReferenceEquals(this, obj))
            {
                return true;
            }
            if (!(obj.GetType() == typeof(HwndObject)))
            {
                return false;
            }
            return this.Equals((HwndObject) obj);
        }

        public bool Equals(HwndObject obj)
        {
            if (object.ReferenceEquals(null, obj))
            {
                return false;
            }
            return (object.ReferenceEquals(this, obj) || obj.Hwnd.Equals(this.Hwnd));
        }

        public HwndObject GetChild(string cls, string title)
        {
            return new HwndObject(HwndInterface.GetHwndChild(this.Hwnd, cls, title));
        }

        public List<HwndObject> GetChildren()
        {
            List<HwndObject> list = new List<HwndObject>();
            foreach (IntPtr ptr in HwndInterface.EnumChildren(this.Hwnd))
            {
                list.Add(new HwndObject(ptr));
            }
            return list;
        }

        public override int GetHashCode()
        {
            return this.Hwnd.GetHashCode();
        }

        public int GetMessageInt(WM msg)
        {
            return HwndInterface.GetMessageInt(this.Hwnd, msg);
        }

        public string GetMessageString(WM msg, uint param)
        {
            return HwndInterface.GetMessageString(this.Hwnd, msg, param);
        }

        public HwndObject GetParent()
        {
            return new HwndObject(HwndInterface.GetHwndParent(this.Hwnd));
        }

        public static HwndObject GetWindowByTitle(string title)
        {
            return new HwndObject(HwndInterface.GetHwndFromTitle(title));
        }

        public static HwndObject GetWindowByClassName(string className)
        {
            return new HwndObject(HwndInterface.GetHwndFromClass(className));
        }

        public static List<HwndObject> GetWindows()
        {
            List<HwndObject> list = new List<HwndObject>();
            foreach (IntPtr ptr in HwndInterface.EnumHwnds())
            {
                list.Add(new HwndObject(ptr));
            }
            return list;
        }

        public static bool operator ==(HwndObject a, HwndObject b)
        {
            if (object.ReferenceEquals(a, null))
            {
                return object.ReferenceEquals(b, null);
            }
            else if (object.ReferenceEquals(b, null))
            {
                return object.ReferenceEquals(a, null);
            }
            return (a.Hwnd == b.Hwnd);
        }

        public static bool operator !=(HwndObject a, HwndObject b)
        {
            return !(a == b);
        }

        public void SendMessage(WM msg, uint param1, string param2)
        {
            HwndInterface.SendMessage(this.Hwnd, msg, param1, param2);
        }

        public void SendMessage(WM msg, uint param1, uint param2)
        {
            HwndInterface.SendMessage(this.Hwnd, msg, param1, param2);
        }

        public override string ToString()
        {
            Point location = this.Location;
            System.Drawing.Size size = this.Size;
            return string.Format("({0}) {1},{2}:{3}x{4} \"{5}\"", new object[] { this.Hwnd, location.X, location.Y, size.Width, size.Height, this.Title });
        }

        public string ClassName
        {
            get
            {
                return HwndInterface.GetHwndClassName(this.Hwnd);
            }
        }

        public IntPtr Hwnd { get; private set; }

        public Point Location
        {
            get
            {
                return HwndInterface.GetHwndPos(this.Hwnd);
            }
            set
            {
                HwndInterface.SetHwndPos(this.Hwnd, value.X, value.Y);
            }
        }

        public System.Drawing.Size Size
        {
            get
            {
                return HwndInterface.GetHwndSize(this.Hwnd);
            }
            set
            {
                HwndInterface.SetHwndSize(this.Hwnd, value.Width, value.Height);
            }
        }

        public string Text
        {
            get
            {
                return HwndInterface.GetHwndText(this.Hwnd);
            }
            set
            {
                HwndInterface.SetHwndText(this.Hwnd, value);
            }
        }

        public string Title
        {
            get
            {
                return HwndInterface.GetHwndTitle(this.Hwnd);
            }
            set
            {
                HwndInterface.SetHwndTitle(this.Hwnd, value);
            }
        }
    }
}


text:
namespace WindowScrape.Types
{
    using System;
    using System.Runtime.InteropServices;

    [StructLayout(LayoutKind.Sequential)]
    internal struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }
}


text:
namespace WindowScrape.Constants
{
    using System;

    [Flags]
    internal enum PositioningFlags
    {
        SWP_ASYNCWINDOWPOS = 0x4000,
        SWP_DEFERERASE = 0x2000,
        SWP_DRAWFRAME = 0x20,
        SWP_FRAMECHANGED = 0x20,
        SWP_HIDEWINDOW = 0x80,
        SWP_NOACTIVATE = 0x10,
        SWP_NOCOPYBITS = 0x100,
        SWP_NOMOVE = 2,
        SWP_NOOWNERZORDER = 0x200,
        SWP_NOREDRAW = 8,
        SWP_NOREPOSITION = 0x200,
        SWP_NOSENDCHANGING = 0x400,
        SWP_NOSIZE = 1,
        SWP_NOZORDER = 4,
        SWP_SHOWWINDOW = 0x40
    }
}


text:
namespace WindowScrape.Constants
{
    using System;

    public enum WM : uint
    {
        ACTIVATE = 6,
        ACTIVATEAPP = 0x1c,
        AFXFIRST = 0x360,
        AFXLAST = 0x37f,
        APP = 0x8000,
        APPCOMMAND = 0x319,
        ASKCBFORMATNAME = 780,
        BN_CLICKED = 0xf5,
        CANCELJOURNAL = 0x4b,
        CANCELMODE = 0x1f,
        CAPTURECHANGED = 0x215,
        CHANGECBCHAIN = 0x30d,
        CHANGEUISTATE = 0x127,
        CHAR = 0x102,
        CHARTOITEM = 0x2f,
        CHILDACTIVATE = 0x22,
        CLEAR = 0x303,
        CLIPBOARDUPDATE = 0x31d,
        CLOSE = 0x10,
        COMMAND = 0x111,
        [Obsolete]
        COMMNOTIFY = 0x44,
        COMPACTING = 0x41,
        COMPAREITEM = 0x39,
        CONTEXTMENU = 0x7b,
        COPY = 0x301,
        COPYDATA = 0x4a,
        CPL_LAUNCH = 0x1400,
        CPL_LAUNCHED = 0x1401,
        CREATE = 1,
        CTLCOLORBTN = 0x135,
        CTLCOLORDLG = 310,
        CTLCOLOREDIT = 0x133,
        CTLCOLORLISTBOX = 0x134,
        CTLCOLORMSGBOX = 0x132,
        CTLCOLORSCROLLBAR = 0x137,
        CTLCOLORSTATIC = 0x138,
        CUT = 0x300,
        DEADCHAR = 0x103,
        DELETEITEM = 0x2d,
        DESTROY = 2,
        DESTROYCLIPBOARD = 0x307,
        DEVICECHANGE = 0x219,
        DEVMODECHANGE = 0x1b,
        DISPLAYCHANGE = 0x7e,
        DRAWCLIPBOARD = 0x308,
        DRAWITEM = 0x2b,
        DROPFILES = 0x233,
        DWMCOLORIZATIONCOLORCHANGED = 800,
        DWMCOMPOSITIONCHANGED = 0x31e,
        DWMNCRENDERINGCHANGED = 0x31f,
        DWMWINDOWMAXIMIZEDCHANGE = 0x321,
        ENABLE = 10,
        ENDSESSION = 0x16,
        ENTERIDLE = 0x121,
        ENTERMENULOOP = 0x211,
        ENTERSIZEMOVE = 0x231,
        ERASEBKGND = 20,
        EXITMENULOOP = 530,
        EXITSIZEMOVE = 0x232,
        FONTCHANGE = 0x1d,
        GETDLGCODE = 0x87,
        GETFONT = 0x31,
        GETHOTKEY = 0x33,
        GETICON = 0x7f,
        GETMINMAXINFO = 0x24,
        GETOBJECT = 0x3d,
        GETTEXT = 13,
        GETTEXTLENGTH = 14,
        GETTITLEBARINFOEX = 0x33f,
        HANDHELDFIRST = 0x358,
        HANDHELDLAST = 0x35f,
        HELP = 0x53,
        HOTKEY = 0x312,
        HSCROLL = 0x114,
        HSCROLLCLIPBOARD = 0x30e,
        ICONERASEBKGND = 0x27,
        IME_CHAR = 0x286,
        IME_COMPOSITION = 0x10f,
        IME_COMPOSITIONFULL = 0x284,
        IME_CONTROL = 0x283,
        IME_ENDCOMPOSITION = 270,
        IME_KEYDOWN = 0x290,
        IME_KEYLAST = 0x10f,
        IME_KEYUP = 0x291,
        IME_NOTIFY = 0x282,
        IME_REQUEST = 0x288,
        IME_SELECT = 0x285,
        IME_SETCONTEXT = 0x281,
        IME_STARTCOMPOSITION = 0x10d,
        INITDIALOG = 0x110,
        INITMENU = 0x116,
        INITMENUPOPUP = 0x117,
        INPUT = 0xff,
        INPUT_DEVICE_CHANGE = 0xfe,
        INPUTLANGCHANGE = 0x51,
        INPUTLANGCHANGEREQUEST = 80,
        KEYDOWN = 0x100,
        KEYFIRST = 0x100,
        KEYLAST = 0x109,
        KEYUP = 0x101,
        KILLFOCUS = 8,
        LBUTTONDBLCLK = 0x203,
        LBUTTONDOWN = 0x201,
        LBUTTONUP = 0x202,
        MBUTTONDBLCLK = 0x209,
        MBUTTONDOWN = 0x207,
        MBUTTONUP = 520,
        MDIACTIVATE = 0x222,
        MDICASCADE = 0x227,
        MDICREATE = 0x220,
        MDIDESTROY = 0x221,
        MDIGETACTIVE = 0x229,
        MDIICONARRANGE = 0x228,
        MDIMAXIMIZE = 0x225,
        MDINEXT = 0x224,
        MDIREFRESHMENU = 0x234,
        MDIRESTORE = 0x223,
        MDISETMENU = 560,
        MDITILE = 550,
        MEASUREITEM = 0x2c,
        MENUCHAR = 0x120,
        MENUCOMMAND = 0x126,
        MENUDRAG = 0x123,
        MENUGETOBJECT = 0x124,
        MENURBUTTONUP = 290,
        MENUSELECT = 0x11f,
        MOUSEACTIVATE = 0x21,
        MOUSEFIRST = 0x200,
        MOUSEHOVER = 0x2a1,
        MOUSEHWHEEL = 0x20e,
        MOUSELAST = 0x20e,
        MOUSELEAVE = 0x2a3,
        MOUSEMOVE = 0x200,
        MOUSEWHEEL = 0x20a,
        MOVE = 3,
        MOVING = 0x216,
        NCACTIVATE = 0x86,
        NCCALCSIZE = 0x83,
        NCCREATE = 0x81,
        NCDESTROY = 130,
        NCHITTEST = 0x84,
        NCLBUTTONDBLCLK = 0xa3,
        NCLBUTTONDOWN = 0xa1,
        NCLBUTTONUP = 0xa2,
        NCMBUTTONDBLCLK = 0xa9,
        NCMBUTTONDOWN = 0xa7,
        NCMBUTTONUP = 0xa8,
        NCMOUSEHOVER = 0x2a0,
        NCMOUSELEAVE = 0x2a2,
        NCMOUSEMOVE = 160,
        NCPAINT = 0x85,
        NCRBUTTONDBLCLK = 0xa6,
        NCRBUTTONDOWN = 0xa4,
        NCRBUTTONUP = 0xa5,
        NCXBUTTONDBLCLK = 0xad,
        NCXBUTTONDOWN = 0xab,
        NCXBUTTONUP = 0xac,
        NEXTDLGCTL = 40,
        NEXTMENU = 0x213,
        NOTIFY = 0x4e,
        NOTIFYFORMAT = 0x55,
        NULL = 0,
        PAINT = 15,
        PAINTCLIPBOARD = 0x309,
        PAINTICON = 0x26,
        PALETTECHANGED = 0x311,
        PALETTEISCHANGING = 0x310,
        PARENTNOTIFY = 0x210,
        PASTE = 770,
        PENWINFIRST = 0x380,
        PENWINLAST = 0x38f,
        [Obsolete]
        POWER = 0x48,
        POWERBROADCAST = 0x218,
        PRINT = 0x317,
        PRINTCLIENT = 0x318,
        QUERYDRAGICON = 0x37,
        QUERYENDSESSION = 0x11,
        QUERYNEWPALETTE = 0x30f,
        QUERYOPEN = 0x13,
        QUERYUISTATE = 0x129,
        QUEUESYNC = 0x23,
        QUIT = 0x12,
        RBUTTONDBLCLK = 0x206,
        RBUTTONDOWN = 0x204,
        RBUTTONUP = 0x205,
        RENDERALLFORMATS = 0x306,
        RENDERFORMAT = 0x305,
        SETCURSOR = 0x20,
        SETFOCUS = 7,
        SETFONT = 0x30,
        SETHOTKEY = 50,
        SETICON = 0x80,
        SETREDRAW = 11,
        SETTEXT = 12,
        SETTINGCHANGE = 0x1a,
        SHOWWINDOW = 0x18,
        SIZE = 5,
        SIZECLIPBOARD = 0x30b,
        SIZING = 0x214,
        SPOOLERSTATUS = 0x2a,
        STYLECHANGED = 0x7d,
        STYLECHANGING = 0x7c,
        SYNCPAINT = 0x88,
        SYSCHAR = 0x106,
        SYSCOLORCHANGE = 0x15,
        SYSCOMMAND = 0x112,
        SYSDEADCHAR = 0x107,
        SYSKEYDOWN = 260,
        SYSKEYUP = 0x105,
        SYSTIMER = 280,
        TABLET_FIRST = 0x2c0,
        TABLET_LAST = 0x2df,
        TCARD = 0x52,
        THEMECHANGED = 0x31a,
        TIMECHANGE = 30,
        TIMER = 0x113,
        UNDO = 0x304,
        UNICHAR = 0x109,
        UNINITMENUPOPUP = 0x125,
        UPDATEUISTATE = 0x128,
        USER = 0x400,
        USERCHANGED = 0x54,
        VKEYTOITEM = 0x2e,
        VSCROLL = 0x115,
        VSCROLLCLIPBOARD = 0x30a,
        WINDOWPOSCHANGED = 0x47,
        WINDOWPOSCHANGING = 70,
        WININICHANGE = 0x1a,
        WTSSESSION_CHANGE = 0x2b1,
        XBUTTONDBLCLK = 0x20d,
        XBUTTONDOWN = 0x20b,
        XBUTTONUP = 0x20c
    }
}


text:
namespace WindowScrape.Static
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Text;
    using WindowScrape.Constants;
    using WindowScrape.Types;

    internal static class HwndInterface
    {
        public static bool ActivateWindow(IntPtr hwnd)
        {
            return SetForegroundWindow(hwnd);
        }

        public static void ClickHwnd(IntPtr hwnd)
        {
            SendMessage(hwnd, 0xf5, IntPtr.Zero, IntPtr.Zero);
        }

        [DllImport("user32.dll")]
        private static extern bool CloseWindow(IntPtr hWnd);
        public static List<IntPtr> EnumChildren(IntPtr hwnd)
        {
            IntPtr zero = IntPtr.Zero;
            List<IntPtr> list = new List<IntPtr>();
            do
            {
                zero = FindWindowEx(hwnd, zero, null, null);
                if (zero != IntPtr.Zero)
                {
                    list.Add(zero);
                }
            }
            while (zero != IntPtr.Zero);
            return list;
        }

        public static List<IntPtr> EnumHwnds()
        {
            return EnumChildren(IntPtr.Zero);
        }

        [DllImport("user32.dll")]
        private static extern int FindWindow(string lpClassName, string lpWindowName);
        [DllImport("user32.dll", SetLastError=true)]
        private static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
        [DllImport("user32.dll", CharSet=CharSet.Auto)]
        private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
        public static IntPtr GetHwnd(string windowText, string className)
        {
            return (IntPtr) FindWindow(className, windowText);
        }

        public static IntPtr GetHwndChild(IntPtr hwnd, string clsName, string ctrlText)
        {
            return FindWindowEx(hwnd, IntPtr.Zero, clsName, ctrlText);
        }

        public static string GetHwndClassName(IntPtr hwnd)
        {
            StringBuilder lpClassName = new StringBuilder(0x100);
            GetClassName(hwnd, lpClassName, lpClassName.MaxCapacity);
            return lpClassName.ToString();
        }

        public static IntPtr GetHwndFromClass(string className)
        {
            return (IntPtr) FindWindow(className, null);
        }

        public static IntPtr GetHwndFromTitle(string windowText)
        {
            return (IntPtr) FindWindow(null, windowText);
        }

        public static IntPtr GetHwndParent(IntPtr hwnd)
        {
            return GetParent(hwnd);
        }

        public static Point GetHwndPos(IntPtr hwnd)
        {
            WindowScrape.Types.RECT lpRect = new WindowScrape.Types.RECT();
            GetWindowRect(hwnd, out lpRect);
            return new Point(lpRect.Left, lpRect.Top);
        }

        public static Size GetHwndSize(IntPtr hwnd)
        {
            WindowScrape.Types.RECT lpRect = new WindowScrape.Types.RECT();
            GetWindowRect(hwnd, out lpRect);
            return new Size(lpRect.Right - lpRect.Left, lpRect.Bottom - lpRect.Top);
        }

        public static string GetHwndText(IntPtr hwnd)
        {
            int capacity = ((int) SendMessage(hwnd, (uint) 14, (uint) 0, (uint) 0)) + 1;
            StringBuilder lParam = new StringBuilder(capacity);
            SendMessage(hwnd, 13, (uint) capacity, lParam);
            return lParam.ToString();
        }

        public static string GetHwndTitle(IntPtr hwnd)
        {
            StringBuilder lpString = new StringBuilder(GetHwndTitleLength(hwnd) + 1);
            GetWindowText(hwnd, lpString, lpString.Capacity);
            return lpString.ToString();
        }

        public static int GetHwndTitleLength(IntPtr hwnd)
        {
            return GetWindowTextLength(hwnd);
        }

        public static int GetMessageInt(IntPtr hwnd, WM msg)
        {
            return (int) SendMessage(hwnd, (uint) msg, (uint) 0, (uint) 0);
        }

        public static string GetMessageString(IntPtr hwnd, WM msg, uint param)
        {
            StringBuilder lParam = new StringBuilder(0x10000);
            SendMessage(hwnd, (uint) msg, param, lParam);
            return lParam.ToString();
        }

        [DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
        private static extern IntPtr GetParent(IntPtr hWnd);
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr hWnd, out WindowScrape.Types.RECT lpRect);
        [DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
        [DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern int GetWindowTextLength(IntPtr hWnd);
        public static bool MinimizeWindow(IntPtr hwnd)
        {
            return CloseWindow(hwnd);
        }

        [DllImport("user32.dll", CharSet=CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll", CharSet=CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, uint wParam, string lParam);
        [DllImport("user32.dll", CharSet=CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, uint wParam, StringBuilder lParam);
        [DllImport("user32.dll", CharSet=CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);
        public static int SendMessage(IntPtr hwnd, WM msg, uint param1, string param2)
        {
            return (int) SendMessage(hwnd, (uint) msg, param1, param2);
        }

        public static int SendMessage(IntPtr hwnd, WM msg, uint param1, uint param2)
        {
            return (int) SendMessage(hwnd, (uint) msg, param1, param2);
        }

        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        public static bool SetHwndPos(IntPtr hwnd, int x, int y)
        {
            return SetWindowPos(hwnd, IntPtr.Zero, x, y, 0, 0, 5);
        }

        public static bool SetHwndSize(IntPtr hwnd, int w, int h)
        {
            return SetWindowPos(hwnd, IntPtr.Zero, 0, 0, w, h, 6);
        }

        public static void SetHwndText(IntPtr hwnd, string text)
        {
            SendMessage(hwnd, (uint) 12, (uint) 0, text);
        }

        public static bool SetHwndTitle(IntPtr hwnd, string text)
        {
            return SetWindowText(hwnd, text);
        }

        [DllImport("user32.dll")]
        private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
        [DllImport("USER32.DLL")]
        private static extern bool SetWindowText(IntPtr hWnd, string lpString);
    }
}