Commit f7d964d7 authored by smartcontracts's avatar smartcontracts Committed by GitHub

feat: add new contracts-periphery package (#2536)

* feat: add new contracts-periphery package

Introduces a new contracts-periphery package and initializes the package
with the RetroReceiver contract (meant to receive RetroPGF funds on all
available chains). Reason for introducing a new package is that the
original contracts package should be reserved for in-protocol contracts
while contracts-periphery can be used for any contracts that we want to
write and test, regardless of if they're part of the protocol.

* feat(ctp): add periphery contracts to ci

Adds contracts-periphery package to CI in the various places it's
required so we can successfully package and release it.
parent 1b7e41f5
---
'@eth-optimism/contracts-periphery': minor
---
Releases the first version of the contracts-periphery package
...@@ -87,6 +87,7 @@ jobs: ...@@ -87,6 +87,7 @@ jobs:
- node_modules - node_modules
- packages/common-ts/node_modules - packages/common-ts/node_modules
- packages/contracts/node_modules - packages/contracts/node_modules
- packages/contracts-periphery/node_modules
- packages/core-utils/node_modules - packages/core-utils/node_modules
- packages/data-transport-layer/node_modules - packages/data-transport-layer/node_modules
- packages/fault-detector/node_modules - packages/fault-detector/node_modules
...@@ -141,6 +142,24 @@ jobs: ...@@ -141,6 +142,24 @@ jobs:
command: yarn test:coverage command: yarn test:coverage
working_directory: packages/contracts working_directory: packages/contracts
contracts-periphery-tests:
docker:
- image: ethereumoptimism/js-builder:latest
resource_class: xlarge
steps:
- restore_cache:
keys:
- v2-cache-yarn-build-{{ .Revision }}
- checkout
- run:
name: Lint
command: yarn lint:check
working_directory: packages/contracts-periphery
- run:
name: Test
command: yarn test:coverage
working_directory: packages/contracts-periphery
dtl-tests: dtl-tests:
docker: docker:
- image: ethereumoptimism/js-builder:latest - image: ethereumoptimism/js-builder:latest
...@@ -178,6 +197,10 @@ jobs: ...@@ -178,6 +197,10 @@ jobs:
name: Check contracts name: Check contracts
command: npx depcheck command: npx depcheck
working_directory: packages/contracts working_directory: packages/contracts
- run:
name: Check contracts-periphery
command: npx depcheck
working_directory: packages/contracts-periphery
- run: - run:
name: Check core-utils name: Check core-utils
command: npx depcheck command: npx depcheck
...@@ -489,6 +512,9 @@ workflows: ...@@ -489,6 +512,9 @@ workflows:
- contracts-tests: - contracts-tests:
requires: requires:
- yarn-monorepo - yarn-monorepo
- contracts-periphery-tests:
requires:
- yarn-monorepo
- js-lint-test: - js-lint-test:
name: dtl-tests name: dtl-tests
package_name: data-transport-layer package_name: data-transport-layer
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
- 'ops/**/*' - 'ops/**/*'
- 'packages/batch-submitter/**/*' - 'packages/batch-submitter/**/*'
- 'packages/contracts/**/*' - 'packages/contracts/**/*'
- 'packages/contracts-periphery/**/*'
- 'packages/data-transport-layer/**/*' - 'packages/data-transport-layer/**/*'
- 'packages/message-relayer/**/*' - 'packages/message-relayer/**/*'
- 'packages/fault-detector/**/*' - 'packages/fault-detector/**/*'
...@@ -25,6 +26,9 @@ M-batch-submitter: ...@@ -25,6 +26,9 @@ M-batch-submitter:
M-contracts: M-contracts:
- any: ['packages/contracts/**/*'] - any: ['packages/contracts/**/*']
M-contracts-periphery:
- any: ['packages/contracts-periphery/**/*']
M-core-utils: M-core-utils:
- any: ['packages/core-utils/**/*'] - any: ['packages/core-utils/**/*']
......
...@@ -18,6 +18,9 @@ packages/contracts/coverage* ...@@ -18,6 +18,9 @@ packages/contracts/coverage*
packages/contracts/@ens* packages/contracts/@ens*
packages/contracts/@openzeppelin* packages/contracts/@openzeppelin*
packages/contracts/hardhat* packages/contracts/hardhat*
packages/contracts-periphery/coverage*
packages/contracts-periphery/@openzeppelin*
packages/contracts-periphery/hardhat*
packages/data-transport-layer/db packages/data-transport-layer/db
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
{"directory": "packages/core-utils", "changeProcessCWD": true }, {"directory": "packages/core-utils", "changeProcessCWD": true },
{"directory": "packages/common-ts", "changeProcessCWD": true }, {"directory": "packages/common-ts", "changeProcessCWD": true },
{"directory": "packages/contracts", "changeProcessCWD": true }, {"directory": "packages/contracts", "changeProcessCWD": true },
{"directory": "packages/contracts-periphery", "changeProcessCWD": true },
{"directory": "packages/data-transport-layer", "changeProcessCWD": true }, {"directory": "packages/data-transport-layer", "changeProcessCWD": true },
{"directory": "packages/batch-submitter", "changeProcessCWD": true }, {"directory": "packages/batch-submitter", "changeProcessCWD": true },
{"directory": "packages/message-relayer", "changeProcessCWD": true }, {"directory": "packages/message-relayer", "changeProcessCWD": true },
......
...@@ -33,6 +33,7 @@ root ...@@ -33,6 +33,7 @@ root
├── <a href="./packages">packages</a> ├── <a href="./packages">packages</a>
│ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript │ ├── <a href="./packages/common-ts">common-ts</a>: Common tools for building apps in TypeScript
│ ├── <a href="./packages/contracts">contracts</a>: L1 and L2 smart contracts for Optimism │ ├── <a href="./packages/contracts">contracts</a>: L1 and L2 smart contracts for Optimism
│ ├── <a href="./packages/contracts-periphery">contracts-periphery</a>: Peripheral contracts for Optimism
│ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier │ ├── <a href="./packages/core-utils">core-utils</a>: Low-level utilities that make building Optimism easier
│ ├── <a href="./packages/data-transport-layer">data-transport-layer</a>: Service for indexing Optimism-related L1 data │ ├── <a href="./packages/data-transport-layer">data-transport-layer</a>: Service for indexing Optimism-related L1 data
│ ├── <a href="./packages/fault-detector">fault-detector</a>: │ ├── <a href="./packages/fault-detector">fault-detector</a>:
......
...@@ -15,6 +15,7 @@ COPY packages/sdk/package.json ./packages/sdk/package.json ...@@ -15,6 +15,7 @@ COPY packages/sdk/package.json ./packages/sdk/package.json
COPY packages/core-utils/package.json ./packages/core-utils/package.json COPY packages/core-utils/package.json ./packages/core-utils/package.json
COPY packages/common-ts/package.json ./packages/common-ts/package.json COPY packages/common-ts/package.json ./packages/common-ts/package.json
COPY packages/contracts/package.json ./packages/contracts/package.json COPY packages/contracts/package.json ./packages/contracts/package.json
COPY packages/contracts-periphery/package.json ./packages/contracts-periphery/package.json
COPY packages/data-transport-layer/package.json ./packages/data-transport-layer/package.json COPY packages/data-transport-layer/package.json ./packages/data-transport-layer/package.json
COPY packages/message-relayer/package.json ./packages/message-relayer/package.json COPY packages/message-relayer/package.json ./packages/message-relayer/package.json
COPY packages/fault-detector/package.json ./packages/fault-detector/package.json COPY packages/fault-detector/package.json ./packages/fault-detector/package.json
......
ignores: [
"@openzeppelin/contracts",
"@rari-capital/solmate",
"@types/node",
"hardhat-deploy",
"ts-node",
"typescript",
"prettier-plugin-solidity",
"solhint-plugin-prettier",
]
# Etherscan API key for Ethereum and Ethereum testnets
ETHERSCAN_API_KEY=
# Etherscan API key for Optimism and Optimism testnets
OPTIMISTIC_ETHERSCAN_API_KEY=
module.exports = {
extends: '../../.eslintrc.js',
}
*.sol linguist-language=Solidity
"*.{ts,js}":
- eslint
"*.sol":
- yarn solhint -f table
module.exports = {
...require('../../.prettierrc.js'),
}
module.exports = {
skipFiles: [
'./test-libraries',
],
mocha: {
grep: "@skip-on-coverage",
invert: true
}
};
{
"extends": "solhint:recommended",
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
"compiler-version": "off",
"code-complexity": ["warn", 5],
"max-line-length": ["error", 100],
"func-param-name-mixedcase": "error",
"modifier-name-mixedcase": "error",
"ordering": "warn",
"not-rely-on-time": "off",
"no-complex-fallback": "off",
"not-rely-on-block-hash": "off",
"reentrancy": "off",
"contract-name-camelcase": "off"
}
}
(The MIT License)
Copyright 2020-2021 Optimism
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Optimism Peripheral Smart Contracts
checks:
- name: eth-gas-reporter/codechecks
settings:
speculativeBranchSelection: false
branches:
- develop
- master
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract TestERC20 is ERC20 {
constructor() ERC20("TEST", "TST") {}
function mint(address to, uint256 value) public {
_mint(to, value);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract TestERC721 is ERC721 {
constructor() ERC721("TEST", "TST") {}
function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { Owned } from "@rari-capital/solmate/src/auth/Owned.sol";
import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol";
/**
* @title RetroReceiver
* @notice RetroReceiver is a minimal contract for receiving funds, meant to be deployed at the
* same address on every chain that supports EIP-2470.
*/
contract RetroReceiver is Owned {
/**
* Emitted when ETH is received by this address.
*/
event ReceivedETH(address indexed from, uint256 amount);
/**
* Emitted when ETH is withdrawn from this address.
*/
event WithdrewETH(address indexed withdrawer, address indexed recipient, uint256 amount);
/**
* Emitted when ERC20 tokens are withdrawn from this address.
*/
event WithdrewERC20(
address indexed withdrawer,
address indexed recipient,
address indexed asset,
uint256 amount
);
/**
* Emitted when ERC721 tokens are withdrawn from this address.
*/
event WithdrewERC721(
address indexed withdrawer,
address indexed recipient,
address indexed asset,
uint256 id
);
/**
* @param _owner Address to initially own the contract.
*/
constructor(address _owner) Owned(_owner) {}
/**
* Make sure we can receive ETH.
*/
receive() external payable {
emit ReceivedETH(msg.sender, msg.value);
}
/**
* Withdraws full ETH balance to the recipient.
*
* @param _to Address to receive the ETH balance.
*/
function withdrawETH(address payable _to) public onlyOwner {
withdrawETH(_to, address(this).balance);
}
/**
* Withdraws partial ETH balance to the recipient.
*
* @param _to Address to receive the ETH balance.
* @param _amount Amount of ETH to withdraw.
*/
function withdrawETH(address payable _to, uint256 _amount) public onlyOwner {
_to.transfer(_amount);
emit WithdrewETH(msg.sender, _to, _amount);
}
/**
* Withdraws full ERC20 balance to the recipient.
*
* @param _asset ERC20 token to withdraw.
* @param _to Address to receive the ERC20 balance.
*/
function withdrawERC20(ERC20 _asset, address _to) public onlyOwner {
withdrawERC20(_asset, _to, _asset.balanceOf(address(this)));
}
/**
* Withdraws partial ERC20 balance to the recipient.
*
* @param _asset ERC20 token to withdraw.
* @param _to Address to receive the ERC20 balance.
* @param _amount Amount of ERC20 to withdraw.
*/
function withdrawERC20(
ERC20 _asset,
address _to,
uint256 _amount
) public onlyOwner {
_asset.transfer(_to, _amount);
emit WithdrewERC20(msg.sender, _to, address(_asset), _amount);
}
/**
* Withdraws ERC721 token to the recipient.
*
* @param _asset ERC721 token to withdraw.
* @param _to Address to receive the ERC721 token.
* @param _id Token ID of the ERC721 token to withdraw.
*/
function withdrawERC721(
ERC721 _asset,
address _to,
uint256 _id
) public onlyOwner {
_asset.transferFrom(address(this), _to, _id);
emit WithdrewERC721(msg.sender, _to, address(_asset), _id);
}
}
import { HardhatUserConfig } from 'hardhat/types'
import { getenv } from '@eth-optimism/core-utils'
import * as dotenv from 'dotenv'
// Hardhat plugins
import '@nomiclabs/hardhat-ethers'
import '@nomiclabs/hardhat-waffle'
import '@nomiclabs/hardhat-etherscan'
import 'solidity-coverage'
// Hardhat tasks
import './tasks'
// Load environment variables from .env
dotenv.config()
const config: HardhatUserConfig = {
networks: {
optimism: {
chainId: 10,
url: 'https://mainnet.optimsim.io',
},
opkovan: {
chainId: 69,
url: 'https://kovan.optimism.io',
},
mainnet: {
chainId: 1,
url: 'https://rpc.ankr.com/eth',
},
},
mocha: {
timeout: 50000,
},
solidity: {
compilers: [
{
version: '0.8.9',
settings: {
optimizer: { enabled: true, runs: 10_000 },
},
},
],
settings: {
metadata: {
bytecodeHash: 'none',
},
outputSelection: {
'*': {
'*': ['metadata', 'storageLayout'],
},
},
},
},
etherscan: {
apiKey: {
mainnet: getenv('ETHERSCAN_API_KEY'),
optimisticEthereum: getenv('OPTIMISTIC_ETHERSCAN_API_KEY'),
optimisticKovan: getenv('OPTIMISTIC_ETHERSCAN_API_KEY'),
},
},
}
export default config
{
"name": "@eth-optimism/contracts-periphery",
"version": "0.0.1",
"description": "[Optimism] External (out-of-protocol) L1 and L2 smart contracts for Optimism",
"main": "dist/index",
"types": "dist/index",
"files": [
"dist/**/*.js",
"dist/**/*.d.ts",
"dist/types/*.ts",
"artifacts/contracts/**/*.json",
"deployments/**/*.json",
"L1",
"L2",
"libraries",
"standards"
],
"scripts": {
"build": "yarn build:contracts",
"build:contracts": "hardhat compile --show-stack-traces",
"test": "yarn test:contracts",
"test:contracts": "hardhat test --show-stack-traces",
"test:coverage": "NODE_OPTIONS=--max_old_space_size=8192 hardhat coverage && istanbul check-coverage --statements 90 --branches 84 --functions 88 --lines 90",
"lint:ts:check": "eslint . --max-warnings=0",
"lint:contracts:check": "yarn solhint -f table 'contracts/**/*.sol'",
"lint:check": "yarn lint:contracts:check && yarn lint:ts:check",
"lint:ts:fix": "eslint --fix .",
"lint:contracts:fix": "yarn prettier --write 'contracts/**/*.sol'",
"lint:fix": "yarn lint:contracts:fix && yarn lint:ts:fix",
"lint": "yarn lint:fix && yarn lint:check",
"clean": "rm -rf ./dist ./artifacts ./cache ./coverage ./tsconfig.build.tsbuildinfo",
"prepublishOnly": "yarn copyfiles -u 1 -e \"**/test-*/**/*\" \"contracts/**/*\" ./",
"postpublish": "rimraf chugsplash L1 L2 libraries standards",
"prepack": "yarn prepublishOnly",
"postpack": "yarn postpublish",
"pre-commit": "lint-staged"
},
"keywords": [
"optimism",
"ethereum",
"contracts",
"periphery",
"solidity"
],
"homepage": "https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-periphery#readme",
"license": "MIT",
"author": "Optimism PBC",
"repository": {
"type": "git",
"url": "https://github.com/ethereum-optimism/optimism.git"
},
"dependencies": {
"@eth-optimism/core-utils": "^0.8.4",
"@openzeppelin/contracts": "4.3.2"
},
"devDependencies": {
"@ethersproject/hardware-wallets": "^5.5.0",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-etherscan": "^3.0.3",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@rari-capital/solmate": "^6.3.0",
"@types/chai": "^4.2.18",
"@types/mocha": "^8.2.2",
"@types/node": "^17.0.21",
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"babel-eslint": "^10.1.0",
"chai": "^4.3.4",
"copyfiles": "^2.3.0",
"dotenv": "^10.0.0",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^35.1.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-unicorn": "^32.0.1",
"ethereum-waffle": "^3.3.0",
"ethers": "^5.5.4",
"hardhat": "^2.9.2",
"hardhat-deploy": "^0.9.3",
"istanbul": "^0.4.5",
"lint-staged": "11.0.0",
"mocha": "^8.4.0",
"prettier": "^2.3.1",
"prettier-plugin-solidity": "^1.0.0-beta.18",
"solhint": "^3.3.6",
"solhint-plugin-prettier": "^0.0.5",
"solidity-coverage": "^0.7.17",
"ts-node": "^10.0.0",
"typescript": "^4.6.2"
}
}
{
"detectors_to_exclude": "conformance-to-solidity-naming-conventions,assembly-usage,low-level-calls,block-timestamp,pragma,solc-version,too-many-digits,boolean-equal,missing-zero-check",
"exclude_informational": false,
"exclude_low": false,
"exclude_medium": false,
"exclude_high": false,
"solc_disable_warnings": false,
"hardhat_ignore_compile": false,
"disable_color": false,
"exclude_dependencies": false,
"filter_paths": "@openzeppelin|hardhat|contracts/test-helpers|contracts/test-libraries|contracts/L2/predeploys/WETH9.sol"
}
import { task } from 'hardhat/config'
import * as types from 'hardhat/internal/core/params/argumentTypes'
import { LedgerSigner } from '@ethersproject/hardware-wallets'
task('deploy-receiver')
.addParam('creator', 'Creator address', undefined, types.string)
.addParam('owner', 'Owner address', undefined, types.string)
.setAction(async (args, hre) => {
console.log(`connecting to ledger...`)
const signer = new LedgerSigner(
hre.ethers.provider,
'default',
hre.ethers.utils.defaultPath
)
const addr = await signer.getAddress()
if (args.creator !== addr) {
throw new Error(`Incorrect key. Creator ${args.creator}, Signer ${addr}`)
}
const singleton = new hre.ethers.Contract(
'0xce0042B868300000d44A59004Da54A005ffdcf9f',
[
{
constant: false,
inputs: [
{
internalType: 'bytes',
name: '_initCode',
type: 'bytes',
},
{
internalType: 'bytes32',
name: '_salt',
type: 'bytes32',
},
],
name: 'deploy',
outputs: [
{
internalType: 'address payable',
name: 'createdContract',
type: 'address',
},
],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
],
signer
)
const salt =
'0x0000000000000000000000000000000000000000000000000000000000000001'
const code = hre.ethers.utils.hexConcat([
hre.artifacts.readArtifactSync('RetroReceiver').bytecode,
hre.ethers.utils.defaultAbiCoder.encode(['address'], [addr]),
])
// Predict and connect to the contract address
const receiver = await hre.ethers.getContractAt(
'RetroReceiver',
await singleton.callStatic.deploy(code, salt, {
gasLimit: 2_000_000,
}),
signer
)
console.log(`creating contract: ${receiver.address}...`)
const tx1 = await singleton.deploy(code, salt, {
gasLimit: 2_000_000,
})
console.log(`waiting for tx: ${tx1.hash}...`)
await tx1.wait()
console.log(`transferring ownership to: ${args.owner}...`)
const tx2 = await receiver.setOwner(args.owner)
console.log(`waiting for tx: ${tx2.hash}...`)
await tx2.wait()
console.log(`verifying contract: ${receiver.address}...`)
await hre.run('verify:verify', {
address: receiver.address,
constructorArguments: [addr],
})
console.log(`all done`)
})
export * from './deploy-receiver'
import hre from 'hardhat'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import { Contract } from 'ethers'
import { expect } from '../../setup'
import { deploy } from '../../helpers'
describe('RetroReceiver', () => {
const DEFAULT_TOKEN_ID = 0
const DEFAULT_AMOUNT = hre.ethers.constants.WeiPerEther
const DEFAULT_RECIPIENT = '0x' + '11'.repeat(20)
let signer1: SignerWithAddress
let signer2: SignerWithAddress
before('signer setup', async () => {
;[signer1, signer2] = await hre.ethers.getSigners()
})
let TestERC20: Contract
let TestERC721: Contract
let RetroReceiver: Contract
beforeEach('deploy contracts', async () => {
TestERC20 = await deploy('TestERC20', { signer: signer1 })
TestERC721 = await deploy('TestERC721', { signer: signer1 })
RetroReceiver = await deploy('RetroReceiver', {
signer: signer1,
args: [signer1.address],
})
})
beforeEach('balance setup', async () => {
await TestERC20.mint(signer1.address, hre.ethers.constants.MaxUint256)
await TestERC721.mint(signer1.address, DEFAULT_TOKEN_ID)
await hre.ethers.provider.send('hardhat_setBalance', [
DEFAULT_RECIPIENT,
'0x0',
])
})
describe('constructor', () => {
it('should set the owner', async () => {
expect(await RetroReceiver.owner()).to.equal(signer1.address)
})
})
describe('receive', () => {
it('should be able to receive ETH', async () => {
await expect(
signer1.sendTransaction({
to: RetroReceiver.address,
value: DEFAULT_AMOUNT,
})
).to.not.be.reverted
expect(
await hre.ethers.provider.getBalance(RetroReceiver.address)
).to.equal(DEFAULT_AMOUNT)
})
})
describe('withdrawETH(address)', () => {
describe('when called by the owner', () => {
it('should withdraw all ETH in the contract', async () => {
await signer1.sendTransaction({
to: RetroReceiver.address,
value: DEFAULT_AMOUNT,
})
await expect(RetroReceiver['withdrawETH(address)'](DEFAULT_RECIPIENT))
.to.emit(RetroReceiver, 'WithdrewETH')
.withArgs(signer1.address, DEFAULT_RECIPIENT, DEFAULT_AMOUNT)
expect(
await hre.ethers.provider.getBalance(RetroReceiver.address)
).to.equal(0)
expect(
await hre.ethers.provider.getBalance(DEFAULT_RECIPIENT)
).to.equal(DEFAULT_AMOUNT)
})
})
describe('when called by not the owner', () => {
it('should revert', async () => {
await expect(
RetroReceiver.connect(signer2)['withdrawETH(address)'](
signer2.address
)
).to.be.revertedWith('UNAUTHORIZED')
})
})
})
describe('withdrawETH(address,uint256)', () => {
describe('when called by the owner', () => {
it('should withdraw the given amount of ETH', async () => {
await signer1.sendTransaction({
to: RetroReceiver.address,
value: DEFAULT_AMOUNT.mul(2),
})
await expect(
RetroReceiver['withdrawETH(address,uint256)'](
DEFAULT_RECIPIENT,
DEFAULT_AMOUNT
)
)
.to.emit(RetroReceiver, 'WithdrewETH')
.withArgs(signer1.address, DEFAULT_RECIPIENT, DEFAULT_AMOUNT)
expect(
await hre.ethers.provider.getBalance(RetroReceiver.address)
).to.equal(DEFAULT_AMOUNT)
expect(
await hre.ethers.provider.getBalance(DEFAULT_RECIPIENT)
).to.equal(DEFAULT_AMOUNT)
})
})
describe('when called by not the owner', () => {
it('should revert', async () => {
await expect(
RetroReceiver.connect(signer2)['withdrawETH(address,uint256)'](
DEFAULT_RECIPIENT,
DEFAULT_AMOUNT
)
).to.be.revertedWith('UNAUTHORIZED')
})
})
})
describe('withdrawERC20(address,address)', () => {
describe('when called by the owner', () => {
it('should withdraw all ERC20 balance held by the contract', async () => {
await TestERC20.transfer(RetroReceiver.address, DEFAULT_AMOUNT)
await expect(
RetroReceiver['withdrawERC20(address,address)'](
TestERC20.address,
DEFAULT_RECIPIENT
)
)
.to.emit(RetroReceiver, 'WithdrewERC20')
.withArgs(
signer1.address,
DEFAULT_RECIPIENT,
TestERC20.address,
DEFAULT_AMOUNT
)
expect(await TestERC20.balanceOf(DEFAULT_RECIPIENT)).to.equal(
DEFAULT_AMOUNT
)
})
})
describe('when called by not the owner', () => {
it('should revert', async () => {
await expect(
RetroReceiver.connect(signer2)['withdrawERC20(address,address)'](
TestERC20.address,
DEFAULT_RECIPIENT
)
).to.be.revertedWith('UNAUTHORIZED')
})
})
})
describe('withdrawERC20(address,address,uint256)', () => {
describe('when called by the owner', () => {
it('should withdraw the given ERC20 amount', async () => {
await TestERC20.transfer(RetroReceiver.address, DEFAULT_AMOUNT.mul(2))
await expect(
RetroReceiver['withdrawERC20(address,address,uint256)'](
TestERC20.address,
DEFAULT_RECIPIENT,
DEFAULT_AMOUNT
)
)
.to.emit(RetroReceiver, 'WithdrewERC20')
.withArgs(
signer1.address,
DEFAULT_RECIPIENT,
TestERC20.address,
DEFAULT_AMOUNT
)
expect(await TestERC20.balanceOf(DEFAULT_RECIPIENT)).to.equal(
DEFAULT_AMOUNT
)
})
})
describe('when called by not the owner', () => {
it('should revert', async () => {
await expect(
RetroReceiver.connect(signer2)[
'withdrawERC20(address,address,uint256)'
](TestERC20.address, DEFAULT_RECIPIENT, DEFAULT_AMOUNT)
).to.be.revertedWith('UNAUTHORIZED')
})
})
})
describe('withdrawERC721', () => {
describe('when called by the owner', () => {
it('should withdraw the token', async () => {
await TestERC721.transferFrom(
signer1.address,
RetroReceiver.address,
DEFAULT_TOKEN_ID
)
await expect(
RetroReceiver.withdrawERC721(
TestERC721.address,
DEFAULT_RECIPIENT,
DEFAULT_TOKEN_ID
)
)
.to.emit(RetroReceiver, 'WithdrewERC721')
.withArgs(
signer1.address,
DEFAULT_RECIPIENT,
TestERC721.address,
DEFAULT_TOKEN_ID
)
expect(await TestERC721.ownerOf(DEFAULT_TOKEN_ID)).to.equal(
DEFAULT_RECIPIENT
)
})
})
describe('when called by not the owner', () => {
it('should revert', async () => {
await expect(
RetroReceiver.connect(signer2).withdrawERC721(
TestERC721.address,
DEFAULT_RECIPIENT,
DEFAULT_TOKEN_ID
)
).to.be.revertedWith('UNAUTHORIZED')
})
})
})
})
import hre from 'hardhat'
export const deploy = async (
name: string,
opts?: {
args?: any[]
signer?: any
}
) => {
const factory = await hre.ethers.getContractFactory(name, opts?.signer)
return factory.deploy(...(opts?.args || []))
}
/* External Imports */
import chai = require('chai')
import Mocha from 'mocha'
import { solidity } from 'ethereum-waffle'
chai.use(solidity)
const should = chai.should()
const expect = chai.expect
export { should, expect, Mocha }
{
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": [
"src/**/*"
]
}
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"resolveJsonModule": true,
}
}
...@@ -975,7 +975,7 @@ ...@@ -975,7 +975,7 @@
"@ethersproject/logger" "^5.5.0" "@ethersproject/logger" "^5.5.0"
bn.js "^4.11.9" bn.js "^4.11.9"
"@ethersproject/bignumber@5.6.1": "@ethersproject/bignumber@5.6.1", "@ethersproject/bignumber@^5.6.0":
version "5.6.1" version "5.6.1"
resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.6.1.tgz#d5e0da518eb82ab8d08ca9db501888bbf5f0c8fb" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.6.1.tgz#d5e0da518eb82ab8d08ca9db501888bbf5f0c8fb"
integrity sha512-UtMeZ3GaUuF9sx2u9nPZiPP3ULcAFmXyvynR7oHl/tPrM+vldZh7ocMsoa1PqKYGnQnqUZJoqxZnGN6J0qdipA== integrity sha512-UtMeZ3GaUuF9sx2u9nPZiPP3ULcAFmXyvynR7oHl/tPrM+vldZh7ocMsoa1PqKYGnQnqUZJoqxZnGN6J0qdipA==
...@@ -993,15 +993,6 @@ ...@@ -993,15 +993,6 @@
"@ethersproject/logger" "^5.4.0" "@ethersproject/logger" "^5.4.0"
bn.js "^4.11.9" bn.js "^4.11.9"
"@ethersproject/bignumber@^5.6.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.6.0.tgz#116c81b075c57fa765a8f3822648cf718a8a0e26"
integrity sha512-VziMaXIUHQlHJmkv1dlcd6GY2PmT0khtAqaMctCIDogxkrarMzA9L94KN1NeXqqOfFD6r0sJT3vCTOFSmZ07DA==
dependencies:
"@ethersproject/bytes" "^5.6.0"
"@ethersproject/logger" "^5.6.0"
bn.js "^4.11.9"
"@ethersproject/bytes@5.4.0", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.4.0": "@ethersproject/bytes@5.4.0", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.4.0":
version "5.4.0" version "5.4.0"
resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.4.0.tgz#56fa32ce3bf67153756dbaefda921d1d4774404e" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.4.0.tgz#56fa32ce3bf67153756dbaefda921d1d4774404e"
...@@ -2997,6 +2988,11 @@ ...@@ -2997,6 +2988,11 @@
dependencies: dependencies:
squirrelly "^8.0.8" squirrelly "^8.0.8"
"@rari-capital/solmate@^6.3.0":
version "6.3.0"
resolved "https://registry.yarnpkg.com/@rari-capital/solmate/-/solmate-6.3.0.tgz#01050e276e71dc4cd4169cf8002b447eb07d90c3"
integrity sha512-SWPbnfZUCe4ahHNqcb0qsPrzzAzMZMoA3x6SxZn04g0dLm0xupVeHonM3LK13uhPGIULF8HzXg8CgXE/fEnMlQ==
"@resolver-engine/core@^0.3.3": "@resolver-engine/core@^0.3.3":
version "0.3.3" version "0.3.3"
resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.3.3.tgz#590f77d85d45bc7ecc4e06c654f41345db6ca967" resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.3.3.tgz#590f77d85d45bc7ecc4e06c654f41345db6ca967"
......
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