Commit 1d91ac6d authored by Mark Tyneway's avatar Mark Tyneway

Merge branch 'develop' into regenesis/0.4.0

parents 7f5936a8 8e2bfd07
---
'@eth-optimism/batch-submitter': patch
'@eth-optimism/data-transport-layer': patch
---
Remove dead imports from core-utils
---
'@eth-optimism/message-relayer': patch
---
Adds a README and cleans up the interface for generating messages and proofs
---
'@eth-optimism/core-utils': patch
---
Delete dead transaction coders. These are no longer used now that RLP encoded transactions are used
---
'@eth-optimism/data-transport-layer': patch
---
Logs the error stacktrace for a failed HTTP request
---
'@eth-optimism/integration-tests': patch
'@eth-optimism/l2geth': patch
'@eth-optimism/core-utils': patch
---
Implement the latest fee spec such that the L2 gas limit is scaled and the tx.gasPrice/tx.gasLimit show correctly in metamask
---
'@eth-optimism/message-relayer': patch
---
Removes spreadsheet mode from the message relayer
......@@ -92,6 +92,14 @@ jobs:
push: true
tags: ethereumoptimism/l2geth:${{ needs.release.outputs.l2geth }}
- name: Publish rpc-proxy
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.rpc-proxy
push: true
tags: ethereumoptimism/rpc-proxy:${{ needs.release.outputs.l2geth }}
# pushes the base builder image to dockerhub
builder:
name: Prepare the base builder image for the services
......
......@@ -17,6 +17,7 @@ module.exports = {
host: '127.0.0.1',
port: 8545,
gasPrice: 0,
gas: 54180127,
}
},
compilers: {
......
......@@ -3,7 +3,7 @@ import chaiAsPromised from 'chai-as-promised'
chai.use(chaiAsPromised)
import { BigNumber, utils } from 'ethers'
import { OptimismEnv } from './shared/env'
import { TxGasLimit } from '@eth-optimism/core-utils'
import { TxGasLimit, TxGasPrice } from '@eth-optimism/core-utils'
describe('Fee Payment Integration Tests', async () => {
let env: OptimismEnv
......@@ -13,9 +13,9 @@ describe('Fee Payment Integration Tests', async () => {
env = await OptimismEnv.new()
})
it('Should return a gasPrice of 1 wei', async () => {
it(`Should return a gasPrice of ${TxGasPrice.toString()} wei`, async () => {
const gasPrice = await env.l2Wallet.getGasPrice()
expect(gasPrice.eq(1))
expect(gasPrice).to.deep.eq(TxGasPrice)
})
it('Should estimateGas with recoverable L2 gasLimit', async () => {
......@@ -28,7 +28,7 @@ describe('Fee Payment Integration Tests', async () => {
utils.parseEther('0.5')
)
const executionGas = await (env.ovmEth
.provider as any).send('eth_estimateExecutionGas', [tx])
.provider as any).send('eth_estimateExecutionGas', [tx, true])
const decoded = TxGasLimit.decode(gas)
expect(BigNumber.from(executionGas)).deep.eq(decoded)
})
......
......@@ -50,13 +50,13 @@ describe('Native ETH Integration Tests', async () => {
const amount = utils.parseEther('0.5')
const addr = '0x' + '1234'.repeat(10)
const gas = await env.ovmEth.estimateGas.transfer(addr, amount)
expect(gas).to.be.deep.eq(BigNumber.from(0x0ef897216d))
expect(gas).to.be.deep.eq(BigNumber.from(6430020))
})
it('Should estimate gas for ETH withdraw', async () => {
const amount = utils.parseEther('0.5')
const gas = await env.ovmEth.estimateGas.withdraw(amount, 0, '0xFFFF')
expect(gas).to.be.deep.eq(BigNumber.from(0x0f5203e9bf))
expect(gas).to.be.deep.eq(BigNumber.from(6140049))
})
})
......
......@@ -134,7 +134,7 @@ describe('Basic RPC tests', () => {
gasPrice: TxGasPrice,
}
const fee = tx.gasPrice.mul(tx.gasLimit)
const gasLimit = 59300000001
const gasLimit = 5920001
await expect(env.l2Wallet.sendTransaction(tx)).to.be.rejectedWith(
`fee too low: ${fee}, use at least tx.gasLimit = ${gasLimit} and tx.gasPrice = ${TxGasPrice.toString()}`
......@@ -213,7 +213,7 @@ describe('Basic RPC tests', () => {
it('correctly exposes revert data for contract calls', async () => {
const req: TransactionRequest = {
...revertingTx,
gasLimit: 59808999999, // override gas estimation
gasLimit: 5980899, // override gas estimation
}
const tx = await wallet.sendTransaction(req)
......@@ -236,7 +236,7 @@ describe('Basic RPC tests', () => {
it('correctly exposes revert data for contract creations', async () => {
const req: TransactionRequest = {
...revertingDeployTx,
gasLimit: 177008999999, // override gas estimation
gasLimit: 17700899, // override gas estimation
}
const tx = await wallet.sendTransaction(req)
......@@ -353,7 +353,7 @@ describe('Basic RPC tests', () => {
to: DEFAULT_TRANSACTION.to,
value: 0,
})
expect(estimate).to.be.eq(0x0dce9004c7)
expect(estimate).to.be.eq(5920012)
})
it('should return a gas estimate that grows with the size of data', async () => {
......@@ -371,6 +371,7 @@ describe('Basic RPC tests', () => {
const estimate = await l2Provider.estimateGas(tx)
const l2Gaslimit = await l2Provider.send('eth_estimateExecutionGas', [
tx,
true,
])
const decoded = TxGasLimit.decode(estimate)
......
......@@ -94,7 +94,6 @@ var (
var (
evictionInterval = time.Minute // Time interval to check for evictable transactions
statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats
gwei = big.NewInt(params.GWei) // 1 gwei, used as a flag for "rollup" transactions
)
var (
......@@ -540,11 +539,15 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
}
// Ensure the transaction doesn't exceed the current block limit gas.
// We skip this condition check if the transaction's gasPrice is set to 1gwei,
// which indicates a "rollup" transaction that's paying for its data.
if pool.currentMaxGas < tx.L2Gas() && tx.GasPrice().Cmp(gwei) != 0 {
if vm.UsingOVM {
if pool.currentMaxGas < tx.L2Gas() {
return ErrGasLimit
}
} else {
if pool.currentMaxGas < tx.Gas() {
return ErrGasLimit
}
}
// Make sure the transaction is signed properly
from, err := types.Sender(pool.signer, tx)
......
......@@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rollup/fees"
)
//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
......@@ -215,7 +216,7 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit }
func (tx *Transaction) L2Gas() uint64 { return tx.data.GasLimit % 100_000_000 }
func (tx *Transaction) L2Gas() uint64 { return fees.DecodeL2GasLimitU64(tx.data.GasLimit) }
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
......
......@@ -1148,9 +1148,17 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h
// EstimateExecutionGas returns an estimate of the amount of gas needed to execute the
// given transaction against the current pending block.
func (s *PublicBlockChainAPI) EstimateExecutionGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
func (s *PublicBlockChainAPI) EstimateExecutionGas(ctx context.Context, args CallArgs, round *bool) (hexutil.Uint64, error) {
blockNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
return legacyDoEstimateGas(ctx, s.b, args, blockNrOrHash, s.b.RPCGasCap())
estimate, err := legacyDoEstimateGas(ctx, s.b, args, blockNrOrHash, s.b.RPCGasCap())
if err != nil {
return estimate, err
}
if round != nil && *round {
rounded := fees.Ceilmod(new(big.Int).SetUint64(uint64(estimate)), fees.BigTenThousand)
estimate = (hexutil.Uint64)(rounded.Uint64())
}
return estimate, nil
}
// ExecutionResult groups all structured logs emitted by the EVM
......
......@@ -3,6 +3,7 @@ package fees
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
)
......@@ -10,12 +11,9 @@ import (
// transaction in gas.
const overhead uint64 = 4200 + 200*params.TxDataNonZeroGasEIP2028
// hundredMillion is a constant used in the gas encoding formula
const hundredMillion uint64 = 100_000_000
// feeScalar is used to scale the calculations in EncodeL2GasLimit
// to prevent them from being too large
const feeScalar uint64 = 1000
const feeScalar uint64 = 10_000_000
// TxGasPrice is a constant that determines the result of `eth_gasPrice`
// It is scaled upwards by 50%
......@@ -26,7 +24,10 @@ const TxGasPrice uint64 = feeScalar + (feeScalar / 2)
// BigTxGasPrice is the L2GasPrice as type big.Int
var BigTxGasPrice = new(big.Int).SetUint64(TxGasPrice)
var bigFeeScalar = new(big.Int).SetUint64(feeScalar)
var bigHundredMillion = new(big.Int).SetUint64(hundredMillion)
const tenThousand = 10000
var BigTenThousand = new(big.Int).SetUint64(tenThousand)
// EncodeTxGasLimit computes the `tx.gasLimit` based on the L1/L2 gas prices and
// the L2 gas limit. The L2 gas limit is encoded inside of the lower order bits
......@@ -40,32 +41,50 @@ var bigHundredMillion = new(big.Int).SetUint64(hundredMillion)
// the fee, so increasing the L2 Gas limit will increase the fee paid.
// The calculation is:
// l1GasLimit = zero_count(data) * 4 + non_zero_count(data) * 16 + overhead
// roundedL2GasLimit = ceilmod(l2GasLimit, 10_000)
// l1Fee = l1GasPrice * l1GasLimit
// l2Fee = l2GasPrice * l2GasLimit
// l2Fee = l2GasPrice * roundedL2GasLimit
// sum = l1Fee + l2Fee
// scaled = sum / scalar
// rounded = ceilmod(scaled, hundredMillion)
// result = rounded + l2GasLimit
// rounded = ceilmod(scaled, tenThousand)
// roundedScaledL2GasLimit = roundedL2GasLimit / tenThousand
// result = rounded + roundedScaledL2GasLimit
// Note that for simplicity purposes, only the calldata is passed into this
// function when in reality the RLP encoded transaction should be. The
// additional cost is added to the overhead constant to prevent the need to RLP
// encode transactions during calls to `eth_estimateGas`
func EncodeTxGasLimit(data []byte, l1GasPrice, l2GasLimit, l2GasPrice *big.Int) *big.Int {
l1GasLimit := calculateL1GasLimit(data, overhead)
roundedL2GasLimit := Ceilmod(l2GasLimit, BigTenThousand)
l1Fee := new(big.Int).Mul(l1GasPrice, l1GasLimit)
l2Fee := new(big.Int).Mul(l2GasPrice, l2GasLimit)
l2Fee := new(big.Int).Mul(l2GasPrice, roundedL2GasLimit)
sum := new(big.Int).Add(l1Fee, l2Fee)
scaled := new(big.Int).Div(sum, bigFeeScalar)
remainder := new(big.Int).Mod(scaled, bigHundredMillion)
scaledSum := new(big.Int).Add(scaled, bigHundredMillion)
rounded := new(big.Int).Sub(scaledSum, remainder)
result := new(big.Int).Add(rounded, l2GasLimit)
rounded := Ceilmod(scaled, BigTenThousand)
roundedScaledL2GasLimit := new(big.Int).Div(roundedL2GasLimit, BigTenThousand)
result := new(big.Int).Add(rounded, roundedScaledL2GasLimit)
return result
}
func Ceilmod(a, b *big.Int) *big.Int {
remainder := new(big.Int).Mod(a, b)
if remainder.Cmp(common.Big0) == 0 {
return a
}
sum := new(big.Int).Add(a, b)
rounded := new(big.Int).Sub(sum, remainder)
return rounded
}
// DecodeL2GasLimit decodes the L2 gas limit from an encoded L2 gas limit
func DecodeL2GasLimit(gasLimit *big.Int) *big.Int {
return new(big.Int).Mod(gasLimit, bigHundredMillion)
scaled := new(big.Int).Mod(gasLimit, BigTenThousand)
return new(big.Int).Mul(scaled, BigTenThousand)
}
func DecodeL2GasLimitU64(gasLimit uint64) uint64 {
scaled := gasLimit % tenThousand
return scaled * tenThousand
}
// calculateL1GasLimit computes the L1 gasLimit based on the calldata and
......
......@@ -74,7 +74,7 @@ var feeTests = map[string]struct {
"max-gaslimit": {
dataLen: 10,
l1GasPrice: params.GWei,
l2GasLimit: 99999999,
l2GasLimit: 99_970_000,
l2GasPrice: params.GWei,
},
"larger-divisor": {
......@@ -95,7 +95,8 @@ func TestCalculateRollupFee(t *testing.T) {
fee := EncodeTxGasLimit(data, l1GasPrice, l2GasLimit, l2GasPrice)
decodedGasLimit := DecodeL2GasLimit(fee)
if l2GasLimit.Cmp(decodedGasLimit) != 0 {
roundedL2GasLimit := Ceilmod(l2GasLimit, BigTenThousand)
if roundedL2GasLimit.Cmp(decodedGasLimit) != 0 {
t.Errorf("rollup fee check failed: expected %d, got %d", l2GasLimit.Uint64(), decodedGasLimit)
}
})
......
......@@ -14,6 +14,8 @@ The base `docker-compose.yml` file will start the required components for a full
Supplementing the base configuration is an additional metric enabling file, `docker-compose-metrics.yml`. Adding this configuration to the stack will enable metric emission for l2geth and start grafana (for metrics visualisation) and influxdb (for metric collection) instances.
Also available for testing is the `rpc-proxy` service in the `docker-compose-rpc-proxy.yml` file. It can be used to restrict what RPC methods are allowed to the Sequencer.
The base stack can be started and stopped with a command like this (there is no need to specify the default docker-compose.yml)
```
docker-compose \
......
version: "3"
services:
rpc-proxy:
depends_on:
- l1_chain
- deployer
- l2geth
image: rpc-proxy
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.rpc-proxy
environment:
SEQUENCER: l2geth:8545
ETH_CALLS_ALLOWED: eth_blockNumber,eth_sendRawTransaction
ports:
- 9546:8080
- 9145:9145
......@@ -80,6 +80,7 @@ services:
- ./envs/geth.env
environment:
ETH1_HTTP: http://l1_chain:8545
ROLLUP_TIMESTAMP_REFRESH: 5s
ROLLUP_STATE_DUMP_PATH: http://deployer:8081/state-dump.latest.json
# used for getting the addresses
URL: http://deployer:8081/addresses.json
......@@ -172,3 +173,4 @@ services:
URL: http://deployer:8081/addresses.json
ENABLE_GAS_REPORT: 1
NO_NETWORK: 1
FROM openresty/openresty:buster
LABEL maintainer="Optimistic Systems <systems@optiomism.io>"
ARG GOTEMPLATE_VERSION=v3.9.0
RUN DEBIAN_FRONTEND=noninteractive apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
openresty-opm \
&& opm get knyar/nginx-lua-prometheus
RUN curl -o /usr/local/bin/gomplate \
-sSL https://github.com/hairyhenderson/gomplate/releases/download/$GOTEMPLATE_VERSION/gomplate_linux-amd64-slim \
&& chmod +x /usr/local/bin/gomplate
RUN mkdir -p /var/log/nginx/ \
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
COPY ./ops/docker/rpc-proxy/eth-jsonrpc-access.lua /usr/local/openresty/nginx/eth-jsonrpc-access.lua
COPY ./ops/docker/rpc-proxy/nginx.template.conf /docker-entrypoint.d/nginx.template.conf
COPY ./ops/docker/rpc-proxy/docker-entrypoint.sh /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"]
#!/bin/bash
set -eo pipefail
if [ -z "$SEQUENCER" ];then
echo "SEQUENCER env must be set, exiting"
exit 1
fi
if [ -z "$ETH_CALLS_ALLOWED" ];then
echo "ETH_CALLS_ALLOWED env must be set, exiting"
exit 1
fi
gomplate -f /docker-entrypoint.d/nginx.template.conf > /usr/local/openresty/nginx/conf/nginx.conf
cat /usr/local/openresty/nginx/conf/nginx.conf
exec openresty "$@"
-- Source: https://github.com/adetante/ethereum-nginx-proxy
local cjson = require('cjson')
local function empty(s)
return s == nil or s == ''
end
local function split(s)
local res = {}
local i = 1
for v in string.gmatch(s, "([^,]+)") do
res[i] = v
i = i + 1
end
return res
end
local function contains(arr, val)
for i, v in ipairs (arr) do
if v == val then
return true
end
end
return false
end
-- parse conf
local blacklist, whitelist = nil
if not empty(ngx.var.jsonrpc_blacklist) then
blacklist = split(ngx.var.jsonrpc_blacklist)
end
if not empty(ngx.var.jsonrpc_whitelist) then
whitelist = split(ngx.var.jsonrpc_whitelist)
end
-- check conf
if blacklist ~= nil and whitelist ~= nil then
ngx.log(ngx.ERR, 'invalid conf: jsonrpc_blacklist and jsonrpc_whitelist are both set')
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
return
end
-- get request content
ngx.req.read_body()
-- try to parse the body as JSON
local success, body = pcall(cjson.decode, ngx.var.request_body);
if not success then
ngx.log(ngx.ERR, 'invalid JSON request')
ngx.exit(ngx.HTTP_BAD_REQUEST)
return
end
local method = body['method']
local version = body['jsonrpc']
-- check we have a method and a version
if empty(method) or empty(version) then
ngx.log(ngx.ERR, 'no method and/or jsonrpc attribute')
ngx.exit(ngx.HTTP_BAD_REQUEST)
return
end
metric_sequencer_requests:inc(1, {method, ngx.var.server_name, ngx.var.status})
-- check the version is supported
if version ~= "2.0" then
ngx.log(ngx.ERR, 'jsonrpc version not supported: ' .. version)
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
return
end
-- if whitelist is configured, check that the method is whitelisted
if whitelist ~= nil then
if not contains(whitelist, method) then
ngx.log(ngx.ERR, 'jsonrpc method is not whitelisted: ' .. method)
ngx.exit(ngx.HTTP_FORBIDDEN)
return
end
end
-- if blacklist is configured, check that the method is not blacklisted
if blacklist ~= nil then
if contains(blacklist, method) then
ngx.log(ngx.ERR, 'jsonrpc method is blacklisted: ' .. method)
ngx.exit(ngx.HTTP_FORBIDDEN)
return
end
end
return
worker_processes 5;
daemon off;
error_log /var/log/nginx/error.log;
worker_rlimit_nofile 8192;
pcre_jit on;
events {
worker_connections 4096;
}
http {
include mime.types;
index index.html;
# See Move default writable paths to a dedicated directory (#119)
# https://github.com/openresty/docker-openresty/issues/119
client_body_temp_path /var/run/openresty/nginx-client-body;
proxy_temp_path /var/run/openresty/nginx-proxy;
fastcgi_temp_path /var/run/openresty/nginx-fastcgi;
uwsgi_temp_path /var/run/openresty/nginx-uwsgi;
scgi_temp_path /var/run/openresty/nginx-scgi;
keepalive_timeout 0;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] $status '
'"$request" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
lua_shared_dict prometheus_metrics 10M;
init_worker_by_lua_block {
prometheus = require("prometheus").init("prometheus_metrics")
metric_requests = prometheus:counter(
"nginx_http_requests_total", "Number of HTTP requests", {"host", "status"})
metric_sequencer_requests = prometheus:counter(
"nginx_eth_sequencer_requests", "Number of requests going to the sequencer", {"method", "host", "status"})
metric_replica_requests = prometheus:counter(
"nginx_eth_replica_requests", "Number of requests going to the replicas", {"host", "status"})
metric_latency = prometheus:histogram(
"nginx_http_request_duration_seconds", "HTTP request latency", {"host"})
metric_connections = prometheus:gauge(
"nginx_http_connections", "Number of HTTP connections", {"state"})
}
log_by_lua_block {
metric_requests:inc(1, {ngx.var.server_name, ngx.var.status})
metric_latency:observe(tonumber(ngx.var.request_time), {ngx.var.server_name})
}
upstream sequencer {
server {{env.Getenv "SEQUENCER"}};
}
server { # RPC proxy server
listen 8080;
location = /healthz {
return 200 'healthz';
}
location / {
set $jsonrpc_whitelist {{env.Getenv "ETH_CALLS_ALLOWED"}};
access_by_lua_file 'eth-jsonrpc-access.lua';
proxy_pass http://sequencer;
}
}
server { # Metrics server
listen 9145;
location /metrics {
content_by_lua_block {
metric_connections:set(ngx.var.connections_reading, {"reading"})
metric_connections:set(ngx.var.connections_waiting, {"waiting"})
metric_connections:set(ngx.var.connections_writing, {"writing"})
prometheus:collect()
}
}
}
}
\ No newline at end of file
......@@ -33,7 +33,6 @@ import {
QueueOrigin,
Batch,
Signature,
TxType,
remove0x,
} from '@eth-optimism/core-utils'
import { Logger, Metrics } from '@eth-optimism/common-ts'
......@@ -252,7 +251,7 @@ describe('BatchSubmitter', () => {
{
rawTransaction: '0x1234',
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: TxType.EIP155,
txType: 0,
queueOrigin: QueueOrigin.Sequencer,
l1TxOrigin: null,
} as any,
......@@ -301,7 +300,7 @@ describe('BatchSubmitter', () => {
{
rawTransaction: '0x1234',
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: TxType.EthSign,
txType: 1,
queueOrigin: QueueOrigin.Sequencer,
l1TxOrigin: null,
} as any,
......@@ -405,7 +404,7 @@ describe('BatchSubmitter', () => {
{
rawTransaction: '0x1234',
l1BlockNumber: nextQueueElement.blockNumber - 1,
txType: TxType.EIP155,
txType: 0,
queueOrigin: QueueOrigin.Sequencer,
l1TxOrigin: null,
} as any,
......
......@@ -6,9 +6,9 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title OVM_GasPriceOracle
* @dev This contract exposes the current execution price, a measure of how congested the network
* @dev This contract exposes the current l2 gas price, a measure of how congested the network
* currently is. This measure is used by the Sequencer to determine what fee to charge for
* transactions. When the system is more congested, the execution price will increase and fees
* transactions. When the system is more congested, the l2 gas price will increase and fees
* will also increase as a result.
*
* Compiler used: optimistic-solc
......@@ -20,8 +20,8 @@ contract OVM_GasPriceOracle is Ownable {
* Variables *
*************/
// Current execution price
uint256 internal executionPrice;
// Current l2 gas price
uint256 public gasPrice;
/***************
* Constructor *
......@@ -31,10 +31,12 @@ contract OVM_GasPriceOracle is Ownable {
* @param _owner Address that will initially own this contract.
*/
constructor(
address _owner
address _owner,
uint256 _initialGasPrice
)
Ownable()
{
setGasPrice(_initialGasPrice);
transferOwnership(_owner);
}
......@@ -44,28 +46,15 @@ contract OVM_GasPriceOracle is Ownable {
********************/
/**
* @return Current execution price.
* Allows the owner to modify the l2 gas price.
* @param _gasPrice New l2 gas price.
*/
function getExecutionPrice()
public
view
returns (
uint256
)
{
return executionPrice;
}
/**
* Allows the owner to modify the execution price.
* @param _executionPrice New execution price.
*/
function setExecutionPrice(
uint256 _executionPrice
function setGasPrice(
uint256 _gasPrice
)
public
onlyOwner
{
executionPrice = _executionPrice;
gasPrice = _gasPrice;
}
}
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
/* Imports: Internal */
import { getContractDefinition } from '../src'
const deployFn: DeployFunction = async (hre: any) => {
const { deployments, getNamedAccounts } = hre
const { deploy } = deployments
const { deployer } = await getNamedAccounts()
const gasPriceOracle = getContractDefinition('OVM_GasPriceOracle', true)
const gasOracleOwner = (hre as any).deployConfig.ovmSequencerAddress
const initialGasPrice = (hre as any).deployConfig.initialGasPriceOracleGasPrice
if (!gasOracleOwner || !initialGasPrice) {
throw new Error('initialGasPrice & ovmSequencerAddress required to deploy gas price oracle')
}
await deploy('OVM_GasPriceOracle', {
contract: gasPriceOracle,
from: deployer,
args: [gasOracleOwner, initialGasPrice],
log: true,
});
}
deployFn.tags = ['OVM_GasPriceOracle']
export default deployFn
# Optimism Regenesis Deployments
## LAYER 2
## OPTIMISTIC-KOVAN
Network : __optimistic-kovan (chain id: 69)__
|Contract|Address|
|--|--|
|OVM_GasPriceOracle|[0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76](https://kovan-optimistic.etherscan.io/address/0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76)|
---
### Chain IDs:
- Mainnet: 10
- Kovan: 69
......
{
"address": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76",
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "_initialGasPrice",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"inputs": [],
"name": "gasPrice",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_gasPrice",
"type": "uint256"
}
],
"name": "setGasPrice",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"transactionHash": "0xed5fd0757566bc0bc1f3f7d701e31199835d6fe7b1e74353ad502983f2f5e744",
"receipt": {
"to": null,
"from": "0x18394B52d3Cb931dfA76F63251919D051953413d",
"contractAddress": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76",
"transactionIndex": 0,
"gasUsed": "1732518",
"logsBloom": "0x00000000000000000000000000000000000000000000000000840000000000000000000000000000000000100000000000000000000000140000000000000000000000000100000000000008000000000001000010000000000000000000000400000000020000000000000000008800000000000000000000400010000000400000000000000000000000000000000000000000002000000000000000000000000000000000000000010000000000000000000000000000000000000000000000008002000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000",
"blockHash": "0x310208064b53df696581d48cee7439d3e94acac8c31a519e5d07e7cc542c920c",
"transactionHash": "0xed5fd0757566bc0bc1f3f7d701e31199835d6fe7b1e74353ad502983f2f5e744",
"logs": [
{
"transactionIndex": 0,
"blockNumber": 336546,
"transactionHash": "0xed5fd0757566bc0bc1f3f7d701e31199835d6fe7b1e74353ad502983f2f5e744",
"address": "0x4200000000000000000000000000000000000006",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000018394b52d3cb931dfa76f63251919d051953413d",
"0x0000000000000000000000004200000000000000000000000000000000000005"
],
"data": "0x0000000000000000000000000000000000000000000000000000000000000000",
"logIndex": 0,
"blockHash": "0x310208064b53df696581d48cee7439d3e94acac8c31a519e5d07e7cc542c920c"
},
{
"transactionIndex": 0,
"blockNumber": 336546,
"transactionHash": "0xed5fd0757566bc0bc1f3f7d701e31199835d6fe7b1e74353ad502983f2f5e744",
"address": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76",
"topics": [
"0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x00000000000000000000000018394b52d3cb931dfa76f63251919d051953413d"
],
"data": "0x",
"logIndex": 1,
"blockHash": "0x310208064b53df696581d48cee7439d3e94acac8c31a519e5d07e7cc542c920c"
},
{
"transactionIndex": 0,
"blockNumber": 336546,
"transactionHash": "0xed5fd0757566bc0bc1f3f7d701e31199835d6fe7b1e74353ad502983f2f5e744",
"address": "0x038a8825A3C3B0c08d52Cc76E5E361953Cf6Dc76",
"topics": [
"0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0",
"0x00000000000000000000000018394b52d3cb931dfa76f63251919d051953413d",
"0x00000000000000000000000018394b52d3cb931dfa76f63251919d051953413d"
],
"data": "0x",
"logIndex": 2,
"blockHash": "0x310208064b53df696581d48cee7439d3e94acac8c31a519e5d07e7cc542c920c"
}
],
"blockNumber": 336546,
"cumulativeGasUsed": "1732518",
"status": 1,
"byzantium": true
},
"args": [
"0x18394B52d3Cb931dfA76F63251919D051953413d",
1000000
],
"bytecode": "0x60806040523480156200001c5760008062000019620002ea565b50505b5060405162000a7338038062000a73833981810160405260408110156200004d576000806200004a620002ea565b50505b81019080805192919060200180519250600091506200006d9050620000ec565b90508060006001816200007f62000357565b816001600160a01b0302191690836001600160a01b0316021790620000a3620003b9565b5050506001600160a01b038116600060008051602062000a5383398151915260405160405180910390a350620000d981620000fe565b620000e48262000184565b50506200044f565b60005a620000f962000408565b905090565b62000108620000ec565b6001600160a01b03166200011b620002c7565b6001600160a01b031614620001705760405162461bcd60e51b8152602060048201819052602482015260008051602062000a338339815191526044820152606401604051809103906200016d620002ea565b50505b808060016200017e620003b9565b50505050565b6200018e620000ec565b6001600160a01b0316620001a1620002c7565b6001600160a01b031614620001f65760405162461bcd60e51b8152602060048201819052602482015260008051602062000a33833981519152604482015260640160405180910390620001f3620002ea565b50505b6001600160a01b038116620002485760405162461bcd60e51b815260040180806020018281038252602681526020018062000a0d602691396040019150506040518091039062000245620002ea565b50505b806001600160a01b03166000806200025f62000357565b906101000a90046001600160a01b03166001600160a01b031660008051602062000a5383398151915260405160405180910390a3806000600181620002a362000357565b816001600160a01b0302191690836001600160a01b03160217906200017e620003b9565b60008080620002d562000357565b906101000a90046001600160a01b0316905090565b632a2a7adb598160e01b8152600481016020815285602082015260005b868110156200032457808601518282016040015260200162000307565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015620003b4576000828201526020016200039b565b505050565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b6000815260206200039b565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b805160008252935060206200039b565b6105ae806200045f6000396000f3fe60806040523480156100195760008061001661042d565b50505b50600436106100605760003560e01c8063715018a61461006e5780638da5cb5b14610078578063bf1fe4201461009c578063f2fde38b146100c2578063fe173b97146100f1575b60008061006b61042d565b50505b61007661010b565b005b61008061020d565b6040516001600160a01b03909116815260200160405180910390f35b610076600480360360208110156100bb576000806100b861042d565b50505b503561022e565b610076600480360360208110156100e1576000806100de61042d565b50505b50356001600160a01b03166102bc565b6100f9610410565b60405190815260200160405180910390f35b61011361041d565b6001600160a01b031661012461020d565b6001600160a01b0316146101875760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016040518091039061018461042d565b50505b60008080610193610498565b906101000a90046001600160a01b03166001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a36000806001816101e6610498565b816001600160a01b0302191690836001600160a01b03160217906102086104f3565b505050565b60008080610219610498565b906101000a90046001600160a01b0316905090565b61023661041d565b6001600160a01b031661024761020d565b6001600160a01b0316146102aa5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401604051809103906102a761042d565b50505b808060016102b66104f3565b50505050565b6102c461041d565b6001600160a01b03166102d561020d565b6001600160a01b0316146103385760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016040518091039061033561042d565b50505b6001600160a01b0381166103865760405162461bcd60e51b8152600401808060200182810382526026815260200180610588602691396040019150506040518091039061038361042d565b50505b806001600160a01b031660008061039b610498565b906101000a90046001600160a01b03166001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a38060006001816103ee610498565b816001600160a01b0302191690836001600160a01b03160217906102b66104f3565b600161041a610498565b81565b60005a610428610541565b905090565b632a2a7adb598160e01b8152600481016020815285602082015260005b8681101561046557808601518282016040015260200161044a565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015610208576000828201526020016104dc565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b6000815260206104dc565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b805160008252935060206104dc56fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573734f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65728be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0",
"deployedBytecode": "0x60806040523480156100195760008061001661042d565b50505b50600436106100605760003560e01c8063715018a61461006e5780638da5cb5b14610078578063bf1fe4201461009c578063f2fde38b146100c2578063fe173b97146100f1575b60008061006b61042d565b50505b61007661010b565b005b61008061020d565b6040516001600160a01b03909116815260200160405180910390f35b610076600480360360208110156100bb576000806100b861042d565b50505b503561022e565b610076600480360360208110156100e1576000806100de61042d565b50505b50356001600160a01b03166102bc565b6100f9610410565b60405190815260200160405180910390f35b61011361041d565b6001600160a01b031661012461020d565b6001600160a01b0316146101875760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016040518091039061018461042d565b50505b60008080610193610498565b906101000a90046001600160a01b03166001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a36000806001816101e6610498565b816001600160a01b0302191690836001600160a01b03160217906102086104f3565b505050565b60008080610219610498565b906101000a90046001600160a01b0316905090565b61023661041d565b6001600160a01b031661024761020d565b6001600160a01b0316146102aa5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401604051809103906102a761042d565b50505b808060016102b66104f3565b50505050565b6102c461041d565b6001600160a01b03166102d561020d565b6001600160a01b0316146103385760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016040518091039061033561042d565b50505b6001600160a01b0381166103865760405162461bcd60e51b8152600401808060200182810382526026815260200180610588602691396040019150506040518091039061038361042d565b50505b806001600160a01b031660008061039b610498565b906101000a90046001600160a01b03166001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a38060006001816103ee610498565b816001600160a01b0302191690836001600160a01b03160217906102b66104f3565b600161041a610498565b81565b60005a610428610541565b905090565b632a2a7adb598160e01b8152600481016020815285602082015260005b8681101561046557808601518282016040015260200161044a565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015610208576000828201526020016104dc565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b6000815260206104dc565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b805160008252935060206104dc56fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373"
}
\ No newline at end of file
......@@ -99,6 +99,12 @@ task('deploy')
undefined,
types.string
)
.addOptionalParam(
'initialGasPriceOracleGasPrice',
'The initial execution price for the gas price oracle.',
undefined,
types.int
)
.setAction(async (args, hre: any, runSuper) => {
// Necessary because hardhat doesn't let us attach non-optional parameters to existing tasks.
const validateAddressArg = (argName: string) => {
......
......@@ -5,6 +5,7 @@ import { ethers } from 'hardhat'
import { ContractFactory, Contract, Signer } from 'ethers'
describe('OVM_GasPriceOracle', () => {
const initialGasPrice = 0
let signer1: Signer
let signer2: Signer
before(async () => {
......@@ -21,7 +22,8 @@ describe('OVM_GasPriceOracle', () => {
let OVM_GasPriceOracle: Contract
beforeEach(async () => {
OVM_GasPriceOracle = await Factory__OVM_GasPriceOracle.deploy(
await signer1.getAddress()
await signer1.getAddress(),
initialGasPrice
)
})
......@@ -33,50 +35,44 @@ describe('OVM_GasPriceOracle', () => {
})
})
describe('setExecutionPrice', () => {
describe('setGasPrice', () => {
it('should revert if called by someone other than the owner', async () => {
await expect(OVM_GasPriceOracle.connect(signer2).setExecutionPrice(1234))
.to.be.reverted
await expect(OVM_GasPriceOracle.connect(signer2).setGasPrice(1234)).to.be
.reverted
})
it('should succeed if called by the owner', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setExecutionPrice(1234))
.to.not.be.reverted
it('should succeed if called by the owner and is equal to `0`', async () => {
await expect(OVM_GasPriceOracle.connect(signer1).setGasPrice(0)).to.not.be
.reverted
})
})
describe('getExecutionPrice', () => {
describe('get gasPrice', () => {
it('should return zero at first', async () => {
expect(await OVM_GasPriceOracle.getExecutionPrice()).to.equal(0)
expect(await OVM_GasPriceOracle.gasPrice()).to.equal(initialGasPrice)
})
it('should change when setExecutionPrice is called', async () => {
const executionPrice = 1234
it('should change when setGasPrice is called', async () => {
const gasPrice = 1234
await OVM_GasPriceOracle.connect(signer1).setExecutionPrice(
executionPrice
)
await OVM_GasPriceOracle.connect(signer1).setGasPrice(gasPrice)
expect(await OVM_GasPriceOracle.getExecutionPrice()).to.equal(
executionPrice
)
expect(await OVM_GasPriceOracle.gasPrice()).to.equal(gasPrice)
})
it('is the 1st storage slot', async () => {
const executionPrice = 1234
const gasPrice = 1234
const slot = 1
// set the price
await OVM_GasPriceOracle.connect(signer1).setExecutionPrice(
executionPrice
)
await OVM_GasPriceOracle.connect(signer1).setGasPrice(gasPrice)
// get the storage slot value
const priceAtSlot = await signer1.provider.getStorageAt(
OVM_GasPriceOracle.address,
slot
)
expect(await OVM_GasPriceOracle.getExecutionPrice()).to.equal(
expect(await OVM_GasPriceOracle.gasPrice()).to.equal(
ethers.BigNumber.from(priceAtSlot)
)
})
......
/* Internal Imports */
import { add0x, remove0x, toVerifiedBytes, encodeHex, getLen } from '../common'
import { Coder, Signature, Uint16, Uint8, Uint24, Address } from './types'
/***********************
* TxTypes and TxData *
**********************/
export enum TxType {
EIP155 = 0,
EthSign = 1,
EthSign2 = 2,
}
export const txTypePlainText = {
0: TxType.EIP155,
1: TxType.EthSign,
2: TxType.EthSign2,
EIP155: TxType.EIP155,
EthSign: TxType.EthSign,
}
export interface DefaultEcdsaTxData {
sig: Signature
gasLimit: Uint16
gasPrice: Uint8
nonce: Uint24
target: Address
data: string
type: TxType
}
export interface EIP155TxData extends DefaultEcdsaTxData {}
export interface EthSignTxData extends DefaultEcdsaTxData {}
/***********************
* Encoding Positions *
**********************/
/*
* The positions in the tx data for the different transaction types
*/
export const TX_TYPE_POSITION = { start: 0, end: 1 }
/*
* The positions in the tx data for the EIP155TxData and EthSignTxData
*/
export const SIGNATURE_FIELD_POSITIONS = {
r: { start: 1, end: 33 }, // 32 bytes
s: { start: 33, end: 65 }, // 32 bytes
v: { start: 65, end: 66 }, // 1 byte
}
export const DEFAULT_ECDSA_TX_FIELD_POSITIONS = {
txType: TX_TYPE_POSITION, // 1 byte
sig: SIGNATURE_FIELD_POSITIONS, // 65 bytes
gasLimit: { start: 66, end: 69 }, // 3 bytes
gasPrice: { start: 69, end: 72 }, // 3 byte
nonce: { start: 72, end: 75 }, // 3 bytes
target: { start: 75, end: 95 }, // 20 bytes
data: { start: 95 }, // byte 95 onward
}
export const EIP155_TX_FIELD_POSITIONS = DEFAULT_ECDSA_TX_FIELD_POSITIONS
export const ETH_SIGN_TX_FIELD_POSITIONS = DEFAULT_ECDSA_TX_FIELD_POSITIONS
export const CTC_TX_GAS_PRICE_MULT_FACTOR = 1_000_000
/***************
* EcdsaCoders *
**************/
class DefaultEcdsaTxCoder implements Coder {
constructor(readonly txType: TxType) {}
public encode(txData: DefaultEcdsaTxData): string {
const txType = encodeHex(
this.txType,
getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.txType)
)
const r = toVerifiedBytes(
txData.sig.r,
getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.sig.r)
)
const s = toVerifiedBytes(
txData.sig.s,
getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.sig.s)
)
const v = encodeHex(
txData.sig.v,
getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.sig.v)
)
const gasLimit = encodeHex(
txData.gasLimit,
getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.gasLimit)
)
if (txData.gasPrice % CTC_TX_GAS_PRICE_MULT_FACTOR !== 0) {
throw new Error(`Gas Price ${txData.gasPrice} cannot be encoded`)
}
const gasPrice = encodeHex(
txData.gasPrice / CTC_TX_GAS_PRICE_MULT_FACTOR,
getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.gasPrice)
)
const nonce = encodeHex(
txData.nonce,
getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.nonce)
)
const target = toVerifiedBytes(
txData.target,
getLen(DEFAULT_ECDSA_TX_FIELD_POSITIONS.target)
)
// Make sure that the data is even
if (txData.data.length % 2 !== 0) {
throw new Error('Non-even hex string for tx data!')
}
const encoding =
'0x' +
txType +
r +
s +
v +
gasLimit +
gasPrice +
nonce +
target +
remove0x(txData.data)
return encoding
}
public decode(txData: string): DefaultEcdsaTxData {
txData = remove0x(txData)
const sliceBytes = (position: { start; end? }): string =>
txData.slice(position.start * 2, position.end * 2)
const pos = DEFAULT_ECDSA_TX_FIELD_POSITIONS
if (parseInt(sliceBytes(pos.txType), 16) !== this.txType) {
throw new Error('Invalid tx type')
}
return {
sig: {
r: add0x(sliceBytes(pos.sig.r)),
s: add0x(sliceBytes(pos.sig.s)),
v: parseInt(sliceBytes(pos.sig.v), 16),
},
gasLimit: parseInt(sliceBytes(pos.gasLimit), 16),
gasPrice:
parseInt(sliceBytes(pos.gasPrice), 16) * CTC_TX_GAS_PRICE_MULT_FACTOR,
nonce: parseInt(sliceBytes(pos.nonce), 16),
target: add0x(sliceBytes(pos.target)),
data: add0x(txData.slice(pos.data.start * 2)),
type: this.txType,
}
}
}
class EthSignTxCoder extends DefaultEcdsaTxCoder {
constructor() {
super(TxType.EthSign)
}
public encode(txData: EthSignTxData): string {
return super.encode(txData)
}
public decode(txData: string): EthSignTxData {
return super.decode(txData)
}
}
class EthSign2TxCoder extends DefaultEcdsaTxCoder {
constructor() {
super(TxType.EthSign2)
}
public encode(txData: EthSignTxData): string {
return super.encode(txData)
}
public decode(txData: string): EthSignTxData {
return super.decode(txData)
}
}
class Eip155TxCoder extends DefaultEcdsaTxCoder {
constructor() {
super(TxType.EIP155)
}
public encode(txData: EIP155TxData): string {
return super.encode(txData)
}
public decode(txData: string): EIP155TxData {
return super.decode(txData)
}
}
/*************
* ctcCoder *
************/
function encode(data: EIP155TxData): string {
if (data.type === TxType.EIP155) {
return new Eip155TxCoder().encode(data)
}
if (data.type === TxType.EthSign) {
return new EthSignTxCoder().encode(data)
}
return null
}
function decode(data: string | Buffer): EIP155TxData {
if (Buffer.isBuffer(data)) {
data = data.toString()
}
data = remove0x(data)
const type = parseInt(data.slice(0, 2), 16)
if (type === TxType.EIP155) {
return new Eip155TxCoder().decode(data)
}
if (type === TxType.EthSign) {
return new EthSignTxCoder().decode(data)
}
if (type === TxType.EthSign2) {
return new EthSign2TxCoder().decode(data)
}
return null
}
/*
* Encoding and decoding functions for all txData types.
*/
export const ctcCoder = {
eip155TxData: new Eip155TxCoder(),
ethSignTxData: new EthSignTxCoder(),
ethSign2TxData: new EthSign2TxCoder(),
encode,
decode,
}
export * from './ecdsa-coder'
export * from './types'
export * from './sequencer-batch'
......@@ -8,8 +8,3 @@ export type Uint16 = number
export type Uint8 = number
export type Uint24 = number
export type Address = string
export interface Coder {
encode: Function
decode: Function
}
......@@ -6,11 +6,12 @@ import { BigNumber } from 'ethers'
import { remove0x } from './common'
const hundredMillion = BigNumber.from(100_000_000)
const feeScalar = 1000
const feeScalar = 10_000_000
export const TxGasPrice = BigNumber.from(feeScalar + feeScalar / 2)
const txDataZeroGas = 4
const txDataNonZeroGasEIP2028 = 16
const overhead = 4200 + 200 * txDataNonZeroGasEIP2028
const tenThousand = BigNumber.from(10_000)
export interface EncodableL2GasLimit {
data: Buffer | string
......@@ -32,21 +33,22 @@ function encode(input: EncodableL2GasLimit): BigNumber {
l2GasPrice = BigNumber.from(l2GasPrice)
}
const l1GasLimit = calculateL1GasLimit(data)
const roundedL2GasLimit = ceilmod(l2GasLimit, tenThousand)
const l1Fee = l1GasLimit.mul(l1GasPrice)
const l2Fee = l2GasLimit.mul(l2GasPrice)
const l2Fee = roundedL2GasLimit.mul(l2GasPrice)
const sum = l1Fee.add(l2Fee)
const scaled = sum.div(feeScalar)
const remainder = scaled.mod(hundredMillion)
const scaledSum = scaled.add(hundredMillion)
const rounded = scaledSum.sub(remainder)
return rounded.add(l2GasLimit)
const rounded = ceilmod(scaled, tenThousand)
const roundedScaledL2GasLimit = roundedL2GasLimit.div(tenThousand)
return rounded.add(roundedScaledL2GasLimit)
}
function decode(fee: BigNumber | number): BigNumber {
if (typeof fee === 'number') {
fee = BigNumber.from(fee)
}
return fee.mod(hundredMillion)
const scaled = fee.mod(tenThousand)
return scaled.mul(tenThousand)
}
export const TxGasLimit = {
......@@ -54,6 +56,22 @@ export const TxGasLimit = {
decode,
}
export function ceilmod(a: BigNumber | number, b: BigNumber | number) {
if (typeof a === 'number') {
a = BigNumber.from(a)
}
if (typeof b === 'number') {
b = BigNumber.from(b)
}
const remainder = a.mod(b)
if (remainder.eq(0)) {
return a
}
const sum = a.add(b)
const rounded = sum.sub(remainder)
return rounded
}
export function calculateL1GasLimit(data: string | Buffer): BigNumber {
const [zeroes, ones] = zeroesAndOnes(data)
const zeroesCost = zeroes * txDataZeroGas
......
......@@ -2,63 +2,13 @@ import '../setup'
/* Internal Imports */
import {
ctcCoder,
encodeAppendSequencerBatch,
decodeAppendSequencerBatch,
TxType,
sequencerBatch,
} from '../../src'
import { expect } from 'chai'
describe('BatchEncoder', () => {
describe('eip155TxData', () => {
it('should encode & then decode to the correct value', () => {
const eip155TxData = {
sig: {
v: 1,
r: '0x' + '11'.repeat(32),
s: '0x' + '22'.repeat(32),
},
gasLimit: 500,
gasPrice: 1000000,
nonce: 100,
target: '0x' + '12'.repeat(20),
data: '0x' + '99'.repeat(10),
type: TxType.EIP155,
}
const encoded = ctcCoder.eip155TxData.encode(eip155TxData)
const decoded = ctcCoder.eip155TxData.decode(encoded)
expect(eip155TxData).to.deep.equal(decoded)
})
it('should fail encoding a bad gas price', () => {
const badGasPrice = 1000001
const eip155TxData = {
sig: {
v: 1,
r: '0x' + '11'.repeat(32),
s: '0x' + '22'.repeat(32),
},
gasLimit: 500,
gasPrice: badGasPrice,
nonce: 100,
target: '0x' + '12'.repeat(20),
data: '0x' + '99'.repeat(10),
type: TxType.EIP155,
}
let error
try {
ctcCoder.eip155TxData.encode(eip155TxData)
} catch (e) {
error = e
}
expect(error.message).to.equal(
`Gas Price ${badGasPrice} cannot be encoded`
)
})
})
describe('appendSequencerBatch', () => {
it('should work with the simple case', () => {
const batch = {
......@@ -100,43 +50,4 @@ describe('BatchEncoder', () => {
}
})
})
describe('generic ctcCoder', () => {
it('should decode EIP155 txs to the correct value', () => {
const eip155TxData = {
sig: {
v: 1,
r: '0x' + '11'.repeat(32),
s: '0x' + '22'.repeat(32),
},
gasLimit: 500,
gasPrice: 1000000,
nonce: 100,
target: '0x' + '12'.repeat(20),
data: '0x' + '99'.repeat(10),
type: TxType.EIP155,
}
const encoded = ctcCoder.encode(eip155TxData)
const decoded = ctcCoder.decode(encoded)
expect(eip155TxData).to.deep.equal(decoded)
})
it('should return null when encoding an unknown type', () => {
const weirdTypeTxData = {
sig: {
v: 1,
r: '0x' + '11'.repeat(32),
s: '0x' + '22'.repeat(32),
},
gasLimit: 500,
gasPrice: 100,
nonce: 100,
target: '0x' + '12'.repeat(20),
data: '0x' + '99'.repeat(10),
type: 420,
}
const encoded = ctcCoder.encode(weirdTypeTxData)
expect(encoded).to.be.null
})
})
})
......@@ -56,7 +56,7 @@ describe('Fees', () => {
dataLen: 10,
l1GasPrice: utils.parseUnits('5', 'ether'),
l2GasPrice: utils.parseUnits('5', 'ether'),
l2GasLimit: 10 ** 8 - 1,
l2GasLimit: 99_970_000,
},
{
name: 'zero-l2-gasprice',
......@@ -113,7 +113,8 @@ describe('Fees', () => {
})
const decoded = fees.TxGasLimit.decode(got)
expect(decoded).to.deep.eq(BigNumber.from(test.l2GasLimit))
const roundedL2GasLimit = fees.ceilmod(test.l2GasLimit, 10_000)
expect(decoded).to.deep.eq(roundedL2GasLimit)
})
}
})
......
......@@ -2,11 +2,9 @@
import { BigNumber, ethers, constants } from 'ethers'
import { getContractFactory } from '@eth-optimism/contracts'
import {
ctcCoder,
fromHexString,
toHexString,
toRpcHexString,
TxType,
EventArgsSequencerBatchAppended,
} from '@eth-optimism/core-utils'
......
......@@ -198,6 +198,7 @@ export class L1TransportServer extends BaseService<L1TransportServerOptions> {
url: req.url,
elapsed,
msg: e.toString(),
stack: e.stack,
})
return res.status(400).json({
error: e.toString(),
......
# @eth-optimism/message-relayer
This package contains:
1. A service for relaying messages from L2 to L1.
2. Utilities for finding these messages and relaying them.
## Installation
```
yarn add @eth-optimism/message-relayer
```
## Relay Utilities
### getMessagesAndProofsForL2Transaction
Finds all L2 => L1 messages sent in a given L2 transaction and generates proof for each.
#### Usage
```typescript
import { getMessagesAndProofsForL2Transaction } from '@eth-optimism/message-relayer'
const main = async () => {
const l1RpcProviderUrl = 'https://layer1.endpoint'
const l2RpcProviderUrl = 'https://layer2.endpoint'
const l1StateCommitmentChainAddress = 'address of OVM_StateCommitmentChain from deployments page'
const l2CrossDomainMessengerAddress = 'address of OVM_L2CrossDomainMessenger from deployments page'
const l2TransactionHash = 'hash of the transaction with messages to relay'
const messagePairs = await getMessagesAndProofsForL2Transaction(
l1RpcProviderUrl,
l2RpcProviderUrl,
l1StateCommitmentChainAddress,
l2CrossDomainMessengerAddress,
l2TransactionHash
)
console.log(messagePairs)
// Will log something along the lines of:
// [
// {
// message: {
// target: '0x...',
// sender: '0x...',
// message: '0x...',
// messageNonce: 1234...
// },
// proof: {
// // complicated
// }
// }
// ]
// You can then do something along the lines of:
// for (const { message, proof } of messagePairs) {
// await l1CrossDomainMessenger.relayMessage(
// message.target,
// message.sender,
// message.message,
// message.messageNonce,
// proof
// )
// }
}
main()
```
{
"name": "@eth-optimism/message-relayer",
"version": "0.1.3",
"private": true,
"description": "[Optimism] Cross Domain Message Relayer service",
"main": "dist/index",
"types": "dist/index",
......@@ -36,7 +35,6 @@
"bcfg": "^0.1.6",
"dotenv": "^8.2.0",
"ethers": "^5.1.0",
"google-spreadsheet": "^3.1.15",
"merkletreejs": "^0.2.18",
"rlp": "^2.2.6"
},
......
import { Wallet, providers } from 'ethers'
import { MessageRelayerService } from '../service'
import { Bcfg } from '@eth-optimism/core-utils'
import SpreadSheet from '../spreadsheet'
import * as dotenv from 'dotenv'
import Config from 'bcfg'
......@@ -49,18 +48,6 @@ const main = async () => {
parseInt(env.FROM_L2_TRANSACTION_INDEX, 10) || 0
)
// Spreadsheet configuration
const SPREADSHEET_MODE = config.bool(
'spreadsheet-mode',
!!env.SPREADSHEET_MODE || false
)
const SHEET_ID = config.str('sheet-id', env.SHEET_ID)
const CLIENT_EMAIL = config.str('client-email', env.CLIENT_EMAIL)
const CLIENT_PRIVATE_KEY = config.str(
'client-private-key',
env.CLIENT_PRIVATE_KEY
)
if (!ADDRESS_MANAGER_ADDRESS) {
throw new Error('Must pass ADDRESS_MANAGER_ADDRESS')
}
......@@ -84,22 +71,6 @@ const main = async () => {
throw new Error('Must pass one of L1_WALLET_KEY or MNEMONIC')
}
let spreadsheet = null
if (SPREADSHEET_MODE) {
if (!SHEET_ID) {
throw new Error('Must pass SHEET_ID')
}
if (!CLIENT_EMAIL) {
throw new Error('Must pass CLIENT_EMAIL')
}
if (!CLIENT_PRIVATE_KEY) {
throw new Error('Must pass CLIENT_PRIVATE_KEY')
}
const privateKey = CLIENT_PRIVATE_KEY.replace(/\\n/g, '\n')
spreadsheet = new SpreadSheet(SHEET_ID)
await spreadsheet.init(CLIENT_EMAIL, privateKey)
}
const service = new MessageRelayerService({
l1RpcProvider: l1Provider,
l2RpcProvider: l2Provider,
......@@ -111,8 +82,6 @@ const main = async () => {
l2BlockOffset: L2_BLOCK_OFFSET,
l1StartOffset: L1_START_OFFSET,
getLogsInterval: GET_LOGS_INTERVAL,
spreadsheetMode: !!SPREADSHEET_MODE,
spreadsheet,
})
await service.start()
......
......@@ -323,12 +323,19 @@ const getStateTrieProof = async (
* @returns An array of messages sent in the transaction and a proof of inclusion for each.
*/
export const getMessagesAndProofsForL2Transaction = async (
l1RpcProvider: ethers.providers.JsonRpcProvider,
l2RpcProvider: ethers.providers.JsonRpcProvider,
l1RpcProvider: ethers.providers.JsonRpcProvider | string,
l2RpcProvider: ethers.providers.JsonRpcProvider | string,
l1StateCommitmentChainAddress: string,
l2CrossDomainMessengerAddress: string,
l2TransactionHash: string
): Promise<CrossDomainMessagePair[]> => {
if (typeof l1RpcProvider === 'string') {
l1RpcProvider = new ethers.providers.JsonRpcProvider(l1RpcProvider)
}
if (typeof l2RpcProvider === 'string') {
l2RpcProvider = new ethers.providers.JsonRpcProvider(l2RpcProvider)
}
const l2Transaction = await l2RpcProvider.getTransaction(l2TransactionHash)
if (l2Transaction === null) {
throw new Error(`unable to find tx with hash: ${l2TransactionHash}`)
......
......@@ -6,7 +6,6 @@ import { MerkleTree } from 'merkletreejs'
/* Imports: Internal */
import { fromHexString, sleep } from '@eth-optimism/core-utils'
import { BaseService } from '@eth-optimism/common-ts'
import SpreadSheet from './spreadsheet'
import {
loadContract,
......@@ -45,10 +44,6 @@ interface MessageRelayerOptions {
// Number of blocks within each getLogs query - max is 2000
getLogsInterval?: number
// Append txs to a spreadsheet instead of submitting transactions
spreadsheetMode?: boolean
spreadsheet?: SpreadSheet
}
const optionSettings = {
......@@ -58,7 +53,6 @@ const optionSettings = {
l2BlockOffset: { default: 1 },
l1StartOffset: { default: 0 },
getLogsInterval: { default: 2000 },
spreadsheetMode: { default: false },
}
export class MessageRelayerService extends BaseService<MessageRelayerOptions> {
......@@ -66,9 +60,6 @@ export class MessageRelayerService extends BaseService<MessageRelayerOptions> {
super('Message_Relayer', options, optionSettings)
}
protected spreadsheetMode: boolean
protected spreadsheet: SpreadSheet
private state: {
lastFinalizedTxHeight: number
nextUnfinalizedTxHeight: number
......@@ -88,7 +79,6 @@ export class MessageRelayerService extends BaseService<MessageRelayerOptions> {
pollingInterval: this.options.pollingInterval,
l2BlockOffset: this.options.l2BlockOffset,
getLogsInterval: this.options.getLogsInterval,
spreadSheetMode: this.options.spreadsheetMode,
})
// Need to improve this, sorry.
this.state = {} as any
......@@ -145,10 +135,6 @@ export class MessageRelayerService extends BaseService<MessageRelayerOptions> {
this.logger.info('Connected to all contracts.')
if (this.options.spreadsheetMode) {
this.logger.info('Running in spreadsheet mode')
}
this.state.lastQueriedL1Block = this.options.l1StartOffset
this.state.eventCache = []
......@@ -498,38 +484,8 @@ export class MessageRelayerService extends BaseService<MessageRelayerOptions> {
message: SentMessage,
proof: SentMessageProof
): Promise<void> {
if (this.options.spreadsheetMode) {
try {
await this.options.spreadsheet.addRow({
target: message.target,
sender: message.sender,
message: message.message,
messageNonce: message.messageNonce.toString(),
encodedMessage: message.encodedMessage,
encodedMessageHash: message.encodedMessageHash,
parentTransactionIndex: message.parentTransactionIndex,
parentTransactionHash: message.parentTransactionIndex,
stateRoot: proof.stateRoot,
batchIndex: proof.stateRootBatchHeader.batchIndex.toString(),
batchRoot: proof.stateRootBatchHeader.batchRoot,
batchSize: proof.stateRootBatchHeader.batchSize.toString(),
prevTotalElements: proof.stateRootBatchHeader.prevTotalElements.toString(),
extraData: proof.stateRootBatchHeader.extraData,
index: proof.stateRootProof.index,
siblings: proof.stateRootProof.siblings.join(','),
stateTrieWitness: proof.stateTrieWitness.toString('hex'),
storageTrieWitness: proof.storageTrieWitness.toString('hex'),
})
this.logger.info('Submitted relay message to spreadsheet')
} catch (e) {
this.logger.error('Cannot submit message to spreadsheet')
this.logger.error(e.message)
}
} else {
try {
this.logger.info(
'Dry-run, checking to make sure proof would succeed...'
)
this.logger.info('Dry-run, checking to make sure proof would succeed...')
await this.state.OVM_L1CrossDomainMessenger.connect(
this.options.l1Wallet
......@@ -544,9 +500,7 @@ export class MessageRelayerService extends BaseService<MessageRelayerOptions> {
}
)
this.logger.info(
'Proof should succeed. Submitting for real this time...'
)
this.logger.info('Proof should succeed. Submitting for real this time...')
} catch (err) {
this.logger.error('Proof would fail, skipping', {
message: err.toString(),
......@@ -593,5 +547,4 @@ export class MessageRelayerService extends BaseService<MessageRelayerOptions> {
}
this.logger.info('Message successfully relayed to Layer 1!')
}
}
}
import { GoogleSpreadsheet } from 'google-spreadsheet'
export default class SpreadSheet {
public doc
public sheet
constructor(id) {
this.doc = new GoogleSpreadsheet(id)
this.sheet = null
}
async init(email, privateKey) {
await this.doc.useServiceAccountAuth({
client_email: email,
private_key: privateKey,
})
await this.doc.loadInfo()
this.sheet = this.doc.sheetsByIndex[0]
}
async addRow(row) {
return this.sheet.addRow(row)
}
}
......@@ -2960,7 +2960,7 @@ arrify@^1.0.0, arrify@^1.0.1:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
arrify@^2.0.0, arrify@^2.0.1:
arrify@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
......@@ -3608,7 +3608,7 @@ base-x@^3.0.2, base-x@^3.0.8:
dependencies:
safe-buffer "^5.0.1"
base64-js@^1.3.0, base64-js@^1.3.1:
base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
......@@ -3901,11 +3901,6 @@ bsert@~0.0.10:
resolved "https://registry.yarnpkg.com/bsert/-/bsert-0.0.10.tgz#231ac82873a1418c6ade301ab5cd9ae385895597"
integrity sha512-NHNwlac+WPy4t2LoNh8pXk8uaIGH3NSaIUbTTRXGpE2WEbq0te/tDykYHkFK57YKLPjv/aGHmbqvnGeVWDz57Q==
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
buffer-from@^1.0.0, buffer-from@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
......@@ -5241,13 +5236,6 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
......@@ -6213,7 +6201,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
assign-symbols "^1.0.0"
is-extendable "^1.0.1"
extend@^3.0.2, extend@~3.0.2:
extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
......@@ -6305,11 +6293,6 @@ fast-safe-stringify@^2.0.7:
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
fast-text-encoding@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53"
integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==
fastq@^1.6.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858"
......@@ -6707,25 +6690,6 @@ gauge@~2.7.3:
strip-ansi "^3.0.1"
wide-align "^1.1.0"
gaxios@^4.0.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.2.0.tgz#33bdc4fc241fc33b8915a4b8c07cfb368b932e46"
integrity sha512-Ms7fNifGv0XVU+6eIyL9LB7RVESeML9+cMvkwGS70xyD6w2Z80wl6RiqiJ9k1KFlJCUTQqFFc8tXmPQfSKUe8g==
dependencies:
abort-controller "^3.0.0"
extend "^3.0.2"
https-proxy-agent "^5.0.0"
is-stream "^2.0.0"
node-fetch "^2.3.0"
gcp-metadata@^4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.2.1.tgz#31849fbcf9025ef34c2297c32a89a1e7e9f2cd62"
integrity sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw==
dependencies:
gaxios "^4.0.0"
json-bigint "^1.0.0"
get-caller-file@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
......@@ -6978,37 +6942,6 @@ globby@^11.0.0, globby@^11.0.2:
merge2 "^1.3.0"
slash "^3.0.0"
google-auth-library@^6.1.3:
version "6.1.6"
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.6.tgz#deacdcdb883d9ed6bac78bb5d79a078877fdf572"
integrity sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==
dependencies:
arrify "^2.0.0"
base64-js "^1.3.0"
ecdsa-sig-formatter "^1.0.11"
fast-text-encoding "^1.0.0"
gaxios "^4.0.0"
gcp-metadata "^4.2.0"
gtoken "^5.0.4"
jws "^4.0.0"
lru-cache "^6.0.0"
google-p12-pem@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.0.3.tgz#673ac3a75d3903a87f05878f3c75e06fc151669e"
integrity sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA==
dependencies:
node-forge "^0.10.0"
google-spreadsheet@^3.1.15:
version "3.1.15"
resolved "https://registry.yarnpkg.com/google-spreadsheet/-/google-spreadsheet-3.1.15.tgz#e7a86f750d8166faaa3e16929561baceb807bf5a"
integrity sha512-S5477f3Gf3Mz6AXgCw7dbaYnzu5aHou1AX4sDqrGboQWnAytkxqJGKQiXN+zzRTTcYzSTJCe0g7KqCPZO9xiOw==
dependencies:
axios "^0.21.1"
google-auth-library "^6.1.3"
lodash "^4.17.20"
got@9.6.0:
version "9.6.0"
resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
......@@ -7061,15 +6994,6 @@ growl@1.10.5:
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
gtoken@^5.0.4:
version "5.2.1"
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.2.1.tgz#4dae1fea17270f457954b4a45234bba5fc796d16"
integrity sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw==
dependencies:
gaxios "^4.0.0"
google-p12-pem "^3.0.3"
jws "^4.0.0"
handlebars@^4.0.1, handlebars@^4.7.6:
version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
......@@ -8114,13 +8038,6 @@ jsesc@~0.5.0:
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
json-bigint@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1"
integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==
dependencies:
bignumber.js "^9.0.0"
json-buffer@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
......@@ -8254,23 +8171,6 @@ just-extend@^4.0.2:
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744"
integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==
jwa@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc"
integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
jws@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4"
integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==
dependencies:
jwa "^2.0.0"
safe-buffer "^5.0.1"
keccak@3.0.1, keccak@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff"
......@@ -9745,7 +9645,7 @@ node-fetch@2.1.2:
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5"
integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=
node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
node-fetch@^2.6.0, node-fetch@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
......@@ -9758,11 +9658,6 @@ node-fetch@~1.7.1:
encoding "^0.1.11"
is-stream "^1.0.1"
node-forge@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
node-gyp-build@^4.2.0:
version "4.2.3"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment