Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
69779a27
Unverified
Commit
69779a27
authored
Feb 08, 2022
by
smartcontracts
Committed by
GitHub
Feb 08, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2163 from ethereum-optimism/sc/sdk-l2-provider
feat(sdk): implement asL2Provider
parents
560036e3
152df378
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
300 additions
and
13 deletions
+300
-13
lucky-dots-nail.md
.changeset/lucky-dots-nail.md
+5
-0
tame-turtles-suffer.md
.changeset/tame-turtles-suffer.md
+5
-0
ovmcontext.spec.ts
integration-tests/test/ovmcontext.spec.ts
+3
-2
queue-ingestion.spec.ts
integration-tests/test/queue-ingestion.spec.ts
+3
-2
rpc.spec.ts
integration-tests/test/rpc.spec.ts
+3
-2
utils.ts
integration-tests/test/shared/utils.ts
+9
-5
package.json
packages/sdk/package.json
+2
-0
index.ts
packages/sdk/src/index.ts
+1
-0
l2-provider.ts
packages/sdk/src/interfaces/l2-provider.ts
+37
-2
l2-provider.ts
packages/sdk/src/l2-provider.ts
+214
-0
coercion.ts
packages/sdk/src/utils/coercion.ts
+18
-0
No files found.
.changeset/lucky-dots-nail.md
0 → 100644
View file @
69779a27
---
'
@eth-optimism/sdk'
:
patch
---
This update implements the asL2Provider function
.changeset/tame-turtles-suffer.md
0 → 100644
View file @
69779a27
---
'
@eth-optimism/integration-tests'
:
patch
---
Use new asL2Provider function for integration tests
integration-tests/test/ovmcontext.spec.ts
View file @
69779a27
/* Imports: External */
import
{
ethers
}
from
'
hardhat
'
import
{
injectL2Context
,
expectApprox
}
from
'
@eth-optimism/core-utils
'
import
{
expectApprox
}
from
'
@eth-optimism/core-utils
'
import
{
predeploys
}
from
'
@eth-optimism/contracts
'
import
{
asL2Provider
}
from
'
@eth-optimism/sdk
'
import
{
Contract
,
BigNumber
}
from
'
ethers
'
/* Imports: Internal */
...
...
@@ -21,7 +22,7 @@ import { Direction } from './shared/watcher-utils'
* must be equal to the blocknumber/timestamp of the L1 transaction.
*/
describe
(
'
OVM Context: Layer 2 EVM Context
'
,
()
=>
{
const
L2Provider
=
injectL2Context
(
l2Provider
)
const
L2Provider
=
asL2Provider
(
l2Provider
)
let
env
:
OptimismEnv
before
(
async
()
=>
{
env
=
await
OptimismEnv
.
new
()
...
...
integration-tests/test/queue-ingestion.spec.ts
View file @
69779a27
/* Imports: Internal */
import
{
providers
}
from
'
ethers
'
import
{
injectL2Context
,
applyL1ToL2Alias
}
from
'
@eth-optimism/core-utils
'
import
{
applyL1ToL2Alias
}
from
'
@eth-optimism/core-utils
'
import
{
asL2Provider
}
from
'
@eth-optimism/sdk
'
/* Imports: External */
import
{
expect
}
from
'
./shared/setup
'
...
...
@@ -13,7 +14,7 @@ describe('Queue Ingestion', () => {
let
l2Provider
:
providers
.
JsonRpcProvider
before
(
async
()
=>
{
env
=
await
OptimismEnv
.
new
()
l2Provider
=
injectL2Context
(
env
.
l2Wallet
.
provider
as
any
)
l2Provider
=
asL2Provider
(
env
.
l2Wallet
.
provider
as
any
)
})
// The batch submitter will notice that there are transactions
...
...
integration-tests/test/rpc.spec.ts
View file @
69779a27
/* Imports: External */
import
{
expectApprox
,
injectL2Context
,
sleep
}
from
'
@eth-optimism/core-utils
'
import
{
expectApprox
,
sleep
}
from
'
@eth-optimism/core-utils
'
import
{
asL2Provider
}
from
'
@eth-optimism/sdk
'
import
{
Wallet
,
BigNumber
,
Contract
,
ContractFactory
,
constants
}
from
'
ethers
'
import
{
serialize
}
from
'
@ethersproject/transactions
'
import
{
ethers
}
from
'
hardhat
'
...
...
@@ -26,7 +27,7 @@ describe('Basic RPC tests', () => {
let
env
:
OptimismEnv
let
wallet
:
Wallet
const
provider
=
injectL2Context
(
l2Provider
)
const
provider
=
asL2Provider
(
l2Provider
)
let
Reverter
:
Contract
let
ValueContext
:
Contract
...
...
integration-tests/test/shared/utils.ts
View file @
69779a27
...
...
@@ -12,8 +12,12 @@ import {
getContractInterface
,
predeploys
,
}
from
'
@eth-optimism/contracts
'
import
{
injectL2Context
,
remove0x
}
from
'
@eth-optimism/core-utils
'
import
{
CrossChainMessenger
,
NumberLike
}
from
'
@eth-optimism/sdk
'
import
{
remove0x
}
from
'
@eth-optimism/core-utils
'
import
{
CrossChainMessenger
,
NumberLike
,
asL2Provider
,
}
from
'
@eth-optimism/sdk
'
import
{
cleanEnv
,
str
,
num
,
bool
,
makeValidator
}
from
'
envalid
'
import
dotenv
from
'
dotenv
'
dotenv
.
config
()
...
...
@@ -112,17 +116,17 @@ export const envConfig = procEnv
export
const
l1Provider
=
new
providers
.
JsonRpcProvider
(
procEnv
.
L1_URL
)
l1Provider
.
pollingInterval
=
procEnv
.
L1_POLLING_INTERVAL
export
const
l2Provider
=
injectL2Context
(
export
const
l2Provider
=
asL2Provider
(
new
providers
.
JsonRpcProvider
(
procEnv
.
L2_URL
)
)
l2Provider
.
pollingInterval
=
procEnv
.
L2_POLLING_INTERVAL
export
const
replicaProvider
=
injectL2Context
(
export
const
replicaProvider
=
asL2Provider
(
new
providers
.
JsonRpcProvider
(
procEnv
.
REPLICA_URL
)
)
replicaProvider
.
pollingInterval
=
procEnv
.
REPLICA_POLLING_INTERVAL
export
const
verifierProvider
=
injectL2Context
(
export
const
verifierProvider
=
asL2Provider
(
new
providers
.
JsonRpcProvider
(
procEnv
.
VERIFIER_URL
)
)
verifierProvider
.
pollingInterval
=
procEnv
.
L2_POLLING_INTERVAL
...
...
packages/sdk/package.json
View file @
69779a27
...
...
@@ -33,6 +33,7 @@
"devDependencies"
:
{
"@ethersproject/abstract-provider"
:
"^5.5.1"
,
"@ethersproject/abstract-signer"
:
"^5.5.0"
,
"@ethersproject/transactions"
:
"^5.5.0"
,
"@nomiclabs/hardhat-ethers"
:
"^2.0.2"
,
"@nomiclabs/hardhat-waffle"
:
"^2.0.1"
,
"@types/chai"
:
"^4.2.18"
,
...
...
@@ -64,6 +65,7 @@
"dependencies"
:
{
"@eth-optimism/contracts"
:
"0.5.13"
,
"@eth-optimism/core-utils"
:
"0.7.6"
,
"lodash"
:
"^4.17.21"
,
"merkletreejs"
:
"^0.2.27"
,
"rlp"
:
"^2.2.7"
},
...
...
packages/sdk/src/index.ts
View file @
69779a27
...
...
@@ -2,3 +2,4 @@ export * from './interfaces'
export
*
from
'
./utils
'
export
*
from
'
./cross-chain-messenger
'
export
*
from
'
./adapters
'
export
*
from
'
./l2-provider
'
packages/sdk/src/interfaces/l2-provider.ts
View file @
69779a27
import
{
Provider
,
TransactionRequest
}
from
'
@ethersproject/abstract-provider
'
import
{
Provider
,
TransactionRequest
,
TransactionResponse
,
Block
,
BlockWithTransactions
,
}
from
'
@ethersproject/abstract-provider
'
import
{
BigNumber
}
from
'
ethers
'
/**
* JSON transaction representation when returned by L2Geth nodes. This is simply an extension to
* the standard transaction response type. You do NOT need to use this type unless you care about
* having typed access to L2-specific fields.
*/
export
interface
L2Transaction
extends
TransactionResponse
{
l1BlockNumber
:
number
l1TxOrigin
:
string
queueOrigin
:
string
rawTransaction
:
string
}
/**
* JSON block representation when returned by L2Geth nodes. Just a normal block but with
* an added stateRoot field.
*/
export
interface
L2Block
extends
Block
{
stateRoot
:
string
}
/**
* JSON block representation when returned by L2Geth nodes. Just a normal block but with
* L2Transaction objects instead of the standard transaction response object.
*/
export
interface
L2BlockWithTransactions
extends
BlockWithTransactions
{
stateRoot
:
string
transactions
:
[
L2Transaction
]
}
/**
* Represents an extended version of an normal ethers Provider that returns additional L2 info and
* has special functions for L2-specific interactions.
*/
export
interface
L2Provider
extends
Provider
{
export
type
L2Provider
<
TProvider
extends
Provider
>
=
TProvider
&
{
/**
* Gets the current L1 (data) gas price.
*
...
...
packages/sdk/src/l2-provider.ts
0 → 100644
View file @
69779a27
import
{
Provider
,
TransactionRequest
}
from
'
@ethersproject/abstract-provider
'
import
{
serialize
}
from
'
@ethersproject/transactions
'
import
{
Contract
,
BigNumber
}
from
'
ethers
'
import
{
predeploys
,
getContractInterface
}
from
'
@eth-optimism/contracts
'
import
cloneDeep
from
'
lodash/cloneDeep
'
import
{
L2Provider
,
ProviderLike
,
NumberLike
}
from
'
./interfaces
'
import
{
toProvider
,
toNumber
,
toBigNumber
}
from
'
./utils
'
/**
* Returns a Contract object for the GasPriceOracle.
*
* @param provider Provider to attach the contract to.
* @returns Contract object for the GasPriceOracle.
*/
const
connectGasPriceOracle
=
(
provider
:
ProviderLike
):
Contract
=>
{
return
new
Contract
(
predeploys
.
OVM_GasPriceOracle
,
getContractInterface
(
'
OVM_GasPriceOracle
'
),
toProvider
(
provider
)
)
}
/**
* Gets the current L1 gas price as seen on L2.
*
* @param l2Provider L2 provider to query the L1 gas price from.
* @returns Current L1 gas price as seen on L2.
*/
export
const
getL1GasPrice
=
async
(
l2Provider
:
ProviderLike
):
Promise
<
BigNumber
>
=>
{
const
gpo
=
connectGasPriceOracle
(
l2Provider
)
return
gpo
.
gasPrice
()
}
/**
* Estimates the amount of L1 gas required for a given L2 transaction.
*
* @param l2Provider L2 provider to query the gas usage from.
* @param tx Transaction to estimate L1 gas for.
* @returns Estimated L1 gas.
*/
export
const
estimateL1Gas
=
async
(
l2Provider
:
ProviderLike
,
tx
:
TransactionRequest
):
Promise
<
BigNumber
>
=>
{
const
gpo
=
connectGasPriceOracle
(
l2Provider
)
return
gpo
.
getL1GasUsed
(
serialize
({
...
tx
,
nonce
:
toNumber
(
tx
.
nonce
as
NumberLike
),
})
)
}
/**
* Estimates the amount of L1 gas cost for a given L2 transaction in wei.
*
* @param l2Provider L2 provider to query the gas usage from.
* @param tx Transaction to estimate L1 gas cost for.
* @returns Estimated L1 gas cost.
*/
export
const
estimateL1GasCost
=
async
(
l2Provider
:
ProviderLike
,
tx
:
TransactionRequest
):
Promise
<
BigNumber
>
=>
{
const
gpo
=
connectGasPriceOracle
(
l2Provider
)
return
gpo
.
getL1Fee
(
serialize
({
...
tx
,
nonce
:
toNumber
(
tx
.
nonce
as
NumberLike
),
})
)
}
/**
* Estimates the L2 gas cost for a given L2 transaction in wei.
*
* @param l2Provider L2 provider to query the gas usage from.
* @param tx Transaction to estimate L2 gas cost for.
* @returns Estimated L2 gas cost.
*/
export
const
estimateL2GasCost
=
async
(
l2Provider
:
ProviderLike
,
tx
:
TransactionRequest
):
Promise
<
BigNumber
>
=>
{
const
parsed
=
toProvider
(
l2Provider
)
const
l2GasPrice
=
await
parsed
.
getGasPrice
()
const
l2GasCost
=
await
parsed
.
estimateGas
(
tx
)
return
l2GasPrice
.
mul
(
l2GasCost
)
}
/**
* Estimates the total gas cost for a given L2 transaction in wei.
*
* @param l2Provider L2 provider to query the gas usage from.
* @param tx Transaction to estimate total gas cost for.
* @returns Estimated total gas cost.
*/
export
const
estimateTotalGasCost
=
async
(
l2Provider
:
ProviderLike
,
tx
:
TransactionRequest
):
Promise
<
BigNumber
>
=>
{
const
l1GasCost
=
await
estimateL1GasCost
(
l2Provider
,
tx
)
const
l2GasCost
=
await
estimateL2GasCost
(
l2Provider
,
tx
)
return
l1GasCost
.
add
(
l2GasCost
)
}
/**
* Returns an provider wrapped as an Optimism L2 provider. Adds a few extra helper functions to
* simplify the process of estimating the gas usage for a transaction on Optimism. Returns a COPY
* of the original provider.
*
* @param provider Provider to wrap into an L2 provider.
* @returns Provider wrapped as an L2 provider.
*/
export
const
asL2Provider
=
<
TProvider
extends
Provider
>
(
provider
:
TProvider
):
L2Provider
<
TProvider
>
=>
{
// Make a copy of the provider since we'll be modifying some internals and don't want to mess
// with the original object.
const
l2Provider
=
cloneDeep
(
provider
)
as
any
// Skip if we've already wrapped this provider.
if
(
l2Provider
.
_isL2Provider
)
{
return
l2Provider
}
// Not exactly sure when the provider wouldn't have a formatter function, but throw an error if
// it doesn't have one. The Provider type doesn't define it but every provider I've dealt with
// seems to have it.
const
formatter
=
l2Provider
.
formatter
if
(
formatter
===
undefined
)
{
throw
new
Error
(
`provider.formatter must be defined`
)
}
// Modify the block formatter to return the state root. Not strictly related to Optimism, just a
// generally useful thing that really should've been on the Ethers block object to begin with.
// TODO: Maybe we should make a PR to add this to the Ethers library?
const
ogBlockFormatter
=
formatter
.
block
.
bind
(
formatter
)
formatter
.
block
=
(
block
:
any
)
=>
{
const
parsed
=
ogBlockFormatter
(
block
)
parsed
.
stateRoot
=
block
.
stateRoot
return
parsed
}
// Modify the block formatter to include all the L2 fields for transactions.
const
ogBlockWithTxFormatter
=
formatter
.
blockWithTransactions
.
bind
(
formatter
)
formatter
.
blockWithTransactions
=
(
block
:
any
)
=>
{
const
parsed
=
ogBlockWithTxFormatter
(
block
)
parsed
.
stateRoot
=
block
.
stateRoot
parsed
.
transactions
=
parsed
.
transactions
.
map
((
tx
:
any
,
idx
:
number
)
=>
{
const
ogTx
=
block
.
transactions
[
idx
]
tx
.
l1BlockNumber
=
ogTx
.
l1BlockNumber
?
toNumber
(
ogTx
.
l1BlockNumber
)
:
ogTx
.
l1BlockNumber
tx
.
l1Timestamp
=
ogTx
.
l1Timestamp
?
toNumber
(
ogTx
.
l1Timestamp
)
:
ogTx
.
l1Timestamp
tx
.
l1TxOrigin
=
ogTx
.
l1TxOrigin
tx
.
queueOrigin
=
ogTx
.
queueOrigin
tx
.
rawTransaction
=
ogTx
.
rawTransaction
return
tx
})
return
parsed
}
// Modify the transaction formatter to include all the L2 fields for transactions.
const
ogTxResponseFormatter
=
formatter
.
transactionResponse
.
bind
(
formatter
)
formatter
.
transactionResponse
=
(
tx
:
any
)
=>
{
const
parsed
=
ogTxResponseFormatter
(
tx
)
parsed
.
txType
=
tx
.
txType
parsed
.
queueOrigin
=
tx
.
queueOrigin
parsed
.
rawTransaction
=
tx
.
rawTransaction
parsed
.
l1TxOrigin
=
tx
.
l1TxOrigin
parsed
.
l1BlockNumber
=
tx
.
l1BlockNumber
?
parseInt
(
tx
.
l1BlockNumber
,
16
)
:
tx
.
l1BlockNumbers
return
parsed
}
// Modify the receipt formatter to include all the L2 fields.
const
ogReceiptFormatter
=
formatter
.
receipt
.
bind
(
formatter
)
formatter
.
receipt
=
(
receipt
:
any
)
=>
{
const
parsed
=
ogReceiptFormatter
(
receipt
)
parsed
.
l1GasPrice
=
toBigNumber
(
receipt
.
l1GasPrice
)
parsed
.
l1GasUsed
=
toBigNumber
(
receipt
.
l1GasUsed
)
parsed
.
l1Fee
=
toBigNumber
(
receipt
.
l1Fee
)
parsed
.
l1FeeScalar
=
parseFloat
(
receipt
.
l1FeeScalar
)
return
parsed
}
// Connect extra functions.
l2Provider
.
getL1GasPrice
=
async
()
=>
{
return
getL1GasPrice
(
l2Provider
)
}
l2Provider
.
estimateL1Gas
=
async
(
tx
:
TransactionRequest
)
=>
{
return
estimateL1Gas
(
l2Provider
,
tx
)
}
l2Provider
.
estimateL1GasCost
=
async
(
tx
:
TransactionRequest
)
=>
{
return
estimateL1GasCost
(
l2Provider
,
tx
)
}
l2Provider
.
estimateL2GasCost
=
async
(
tx
:
TransactionRequest
)
=>
{
return
estimateL2GasCost
(
l2Provider
,
tx
)
}
l2Provider
.
estimateTotalGasCost
=
async
(
tx
:
TransactionRequest
)
=>
{
return
estimateTotalGasCost
(
l2Provider
,
tx
)
}
l2Provider
.
_isL2Provider
=
true
return
l2Provider
}
packages/sdk/src/utils/coercion.ts
View file @
69779a27
...
...
@@ -10,6 +10,7 @@ import { ethers, BigNumber } from 'ethers'
import
{
SignerOrProviderLike
,
ProviderLike
,
TransactionLike
,
NumberLike
,
AddressLike
,
...
...
@@ -36,6 +37,23 @@ export const toSignerOrProvider = (
}
}
/**
* Converts a ProviderLike into a Provider. Assumes that if the input is a string then it is a
* JSON-RPC url.
*
* @param provider ProviderLike to turn into a Provider.
* @returns Input as a Provider.
*/
export
const
toProvider
=
(
provider
:
ProviderLike
):
Provider
=>
{
if
(
typeof
provider
===
'
string
'
)
{
return
new
ethers
.
providers
.
JsonRpcProvider
(
provider
)
}
else
if
(
Provider
.
isProvider
(
provider
))
{
return
provider
as
Provider
}
else
{
throw
new
Error
(
'
Invalid provider
'
)
}
}
/**
* Pulls a transaction hash out of a TransactionLike object.
*
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment