Skip to content

Commit

Permalink
📦 Introduce subpackage for Proof Of Reserve feature development (#1189)
Browse files Browse the repository at this point in the history
Co-authored-by: Michał Sieczkowski <michal.sieczkowski@trusttoken.com>
  • Loading branch information
msieczko and Michał Sieczkowski authored Sep 23, 2022
1 parent 9215ad2 commit dc41baa
Show file tree
Hide file tree
Showing 15 changed files with 2,360 additions and 37 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages/
13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
"name": "trusttoken-smart-contracts",
"version": "2.0.0",
"description": "TrueFi and True Currency Smart Contracts",
"private": true,
"workspaces": [
"packages/*"
],
"scripts": {
"postinstall": "patch-package",
"flatten": "./flatten.sh",
Expand Down Expand Up @@ -42,9 +46,14 @@
"slither": "./slither.sh",
"coverage": "bash ./runCoverage.sh"
},
"author": "",
"license": "SEE LICENSE IN LICENSE.md",
"devDependencies": {
"@ethersproject/abi": "5.0.7",
"@ethersproject/constants": "5.0.5",
"@ethersproject/keccak256": "5.0.4",
"@ethersproject/providers": "5.0.13",
"@ethersproject/strings": "5.0.5",
"@ethersproject/units": "5.0.6",
"@typechain/ethers-v5": "^6.0.5",
"@types/chai": "^4.2.11",
"@types/mocha": "^7.0.2",
Expand Down Expand Up @@ -85,8 +94,6 @@
"web3": "1.2.4"
},
"resolutions": {
"ethereum-waffle/**/ganache-core": "^2.13.0",
"@ethereum-waffle/provider/ganache-core": "^2.13.0",
"**/@resolver-engine/core": "^0.3.3"
}
}
9 changes: 9 additions & 0 deletions packages/contracts-por/.compiler.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"version": "0.8.16",
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
}
}
}
3 changes: 3 additions & 0 deletions packages/contracts-por/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/node_modules
/build
/cache
8 changes: 8 additions & 0 deletions packages/contracts-por/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"require": ["ts-node/register/transpile-only", "tsconfig-paths/register"],
"extension": ["ts"],
"target": "esnext",
"timeout": 40000,
"watch-files": ["test"],
"exit": true
}
70 changes: 70 additions & 0 deletions packages/contracts-por/abi-exporter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const fs = require('fs')
const path = require('path')
const { extendConfig } = require('hardhat/config')

const { HardhatPluginError } = require('hardhat/plugins')

const {
TASK_COMPILE,
} = require('hardhat/builtin-tasks/task-names')

extendConfig(function (config, userConfig) {
config.abiExporter = Object.assign(
{
path: './abi',
clear: false,
flat: false,
only: [],
except: [],
spacing: 2,
},
userConfig.abiExporter,
)
})

task(TASK_COMPILE, async function (args, hre, runSuper) {
const config = hre.config.abiExporter

await runSuper()

const outputDirectory = path.resolve(hre.config.paths.root, config.path)

if (!outputDirectory.startsWith(hre.config.paths.root)) {
throw new HardhatPluginError('resolved path must be inside of project directory')
}

if (outputDirectory === hre.config.paths.root) {
throw new HardhatPluginError('resolved path must not be root directory')
}

if (config.clear) {
if (fs.existsSync(outputDirectory)) {
fs.rmdirSync(outputDirectory, { recursive: true })
}
}

if (!fs.existsSync(outputDirectory)) {
fs.mkdirSync(outputDirectory, { recursive: true })
}

for (const fullName of await hre.artifacts.getAllFullyQualifiedNames()) {
if (config.only.length && !config.only.some(m => fullName.match(m))) continue
if (config.except.length && config.except.some(m => fullName.match(m))) continue

const { abi, sourceName, contractName, bytecode, deployedBytecode } = await hre.artifacts.readArtifact(fullName)

if (!abi.length) continue

const destination = path.resolve(
outputDirectory,
config.flat ? '' : sourceName,
contractName,
) + '.json'

if (!fs.existsSync(path.dirname(destination))) {
fs.mkdirSync(path.dirname(destination), { recursive: true })
}

fs.writeFileSync(destination, `${JSON.stringify({ abi, bytecode, deployedBytecode }, null, config.spacing)}\n`, { flag: 'w' })
}
})
10 changes: 10 additions & 0 deletions packages/contracts-por/contracts/DummyContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

contract DummyContract {
uint256 private value = 5;

function getValue() public view returns (uint256) {
return value;
}
}
30 changes: 30 additions & 0 deletions packages/contracts-por/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import '@typechain/hardhat'
import '@nomiclabs/hardhat-waffle'
import './abi-exporter'

import compiler from './.compiler.json'

module.exports = {
paths: {
sources: './contracts',
artifacts: './build',
cache: './cache',
},
abiExporter: {
path: './build',
flat: true,
spacing: 2,
},
networks: {
hardhat: {
allowUnlimitedContractSize: true,
},
},
typechain: {
outDir: 'build/types',
target: 'ethers-v5',
},
solidity: {
compilers: [compiler],
},
}
37 changes: 37 additions & 0 deletions packages/contracts-por/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@trusttoken-smart-contracts/contracts-por",
"version": "0.0.1",
"description": "Updates to TUSD contracts for Proof of Reserve feature",
"private": true,
"scripts": {
"clean": "rm -rf ./build && hardhat clean",
"build:hardhat": "hardhat compile",
"build:typechain": "typechain --target ethers-v5 --out-dir build/types 'build/*.json'",
"build": "yarn clean && yarn build:hardhat && yarn build:typechain && mars",
"test": "mocha 'test/**/*.test.ts'"
},
"dependencies": {
"ethereum-mars": "0.2.5"
},
"devDependencies": {
"@ethersproject/abi": "^5.7.0",
"@ethersproject/bytes": "^5.7.0",
"@ethersproject/providers": "^5.7.0",
"@nomiclabs/hardhat-waffle": "^2.0.3",
"@typechain/ethers-v5": "^10.0.0",
"@typechain/hardhat": "^6.0.0",
"@types/chai": "^4.3.3",
"@types/mocha": "^9.1.1",
"@types/node": "^17.0.34",
"chai": "^4.3.6",
"ethereum-waffle": "4.0.7",
"ethers": "^5.7.0",
"hardhat": "~2.10.2",
"mocha": "^10.0.0",
"solc": "0.8.16",
"ts-node": "^10.7.0",
"tsconfig-paths": "^4.1.0",
"typechain": "^8.0.0",
"typescript": "4.5.4"
}
}
15 changes: 15 additions & 0 deletions packages/contracts-por/test/DummyContract.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { expect, use } from 'chai'
import { solidity } from 'ethereum-waffle'
import { setupFixtureLoader } from './setup'
import { dummyContractFixture } from 'fixtures/dummyContractFixture'

use(solidity)

describe('DummyContract', () => {
const loadFixture = setupFixtureLoader()

it('should have a constructor', async () => {
const { dummyContract } = await loadFixture(dummyContractFixture)
expect(await dummyContract.getValue()).to.eq(5)
})
})
7 changes: 7 additions & 0 deletions packages/contracts-por/test/fixtures/dummyContractFixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { DummyContract__factory } from 'build/types'
import { Wallet } from 'ethers'

export async function dummyContractFixture([wallet]: Wallet[]) {
const dummyContract = await new DummyContract__factory(wallet).deploy()
return { dummyContract }
}
50 changes: 50 additions & 0 deletions packages/contracts-por/test/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Fixture, MockProvider } from 'ethereum-waffle'
import { waffle } from 'hardhat'
import { Wallet } from 'ethers'
import './utils/hardhatPatches.ts'

type FixtureLoader = ReturnType<typeof waffle.createFixtureLoader>
interface FixtureReturns {
provider: MockProvider,
wallet: Wallet,
other: Wallet,
another: Wallet,
}

let loadFixture: ReturnType<typeof setupOnce> | undefined
export function setupFixtureLoader() {
if (!loadFixture) {
loadFixture = setupOnce()
}
return loadFixture
}

type CurrentLoader = { loader: FixtureLoader, returns: FixtureReturns, fixture: Fixture<any> }

function setupOnce() {
let currentLoader: CurrentLoader = {
loader: {} as FixtureLoader,
returns: {} as FixtureReturns,
fixture: {} as Fixture<any>,
}

async function makeLoader(): Promise<{ loader: FixtureLoader, returns: FixtureReturns }> {
const { provider } = waffle
await provider.send('hardhat_reset', [])
const [wallet, other, another, ...rest] = provider.getWallets()
const loader = waffle.createFixtureLoader([wallet, other, another, ...rest], provider)
const returns = { provider, wallet, other, another }
return { loader, returns }
}

async function loadFixture<T>(fixture: Fixture<T>): Promise<T & FixtureReturns> {
// This function creates a new provider for each fixture, because of bugs
// in ganache that clear contract code on evm_revert
const { loader, returns } = currentLoader.fixture === fixture ? currentLoader : await makeLoader()
currentLoader = { fixture, loader, returns }
const result = await loader(fixture)
return { ...returns, ...result }
}

return loadFixture
}
82 changes: 82 additions & 0 deletions packages/contracts-por/test/utils/hardhatPatches.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { waffle } from 'hardhat'
import type { RecordedCall } from 'ethereum-waffle'
import { utils } from 'ethers'

const init = (waffle.provider as any)._hardhatNetwork.provider._wrapped._wrapped._wrapped._init

function patchSkipGasCostCheck() {
const originalProcess = (waffle.provider as any)._hardhatNetwork.provider._wrapped._wrapped._wrapped._ethModule.processRequest.bind(
(waffle.provider as any)._hardhatNetwork.provider._wrapped._wrapped._wrapped._ethModule,
)
;(waffle.provider as any)._hardhatNetwork.provider._wrapped._wrapped._wrapped._ethModule.processRequest = (
method: string,
params: any[],
) => {
if (method === 'eth_estimateGas') {
return '0xB71B00'
} else {
return originalProcess(method, params)
}
}
}

class CallHistory {
recordedCalls: RecordedCall[] = [];

addUniqueCall(call: RecordedCall) {
if (!this.recordedCalls.find(c => c.address === call.address && c.data === call.data)) {
this.recordedCalls.push(call)
}
}

clearAll() {
this.recordedCalls = []
}
}

function toRecordedCall(message: any): RecordedCall {
return {
address: message.to?.buf ? utils.getAddress(utils.hexlify(message.to.buf)) : undefined,
data: message.data ? utils.hexlify(message.data) : '0x',
}
}

const callHistory = new CallHistory();
(waffle.provider as any).clearCallHistory = () => {
callHistory.clearAll()
}

let beforeMessageListener: (message: any) => void | undefined;

(waffle.provider as any)._hardhatNetwork.provider._wrapped._wrapped._wrapped._init = async function () {
await init.apply(this)
if (typeof beforeMessageListener === 'function') {
// hast to be here because of weird behaviour of init function
(waffle.provider as any)
._hardhatNetwork
.provider
._wrapped
._wrapped
._wrapped
._node
._vmTracer
._vm
.off('beforeMessage', beforeMessageListener)
}
if ((waffle.provider as any)._hardhatNetwork.provider._wrapped._wrapped._wrapped._node._vmTracer._vm.listenerCount('beforeMessage') < 2) {
patchSkipGasCostCheck()
}
beforeMessageListener = (message: any) => {
callHistory.addUniqueCall(toRecordedCall(message))
}
const provider: any = waffle.provider
provider.callHistory = callHistory.recordedCalls;
(waffle.provider as any)
._hardhatNetwork.provider
._wrapped._wrapped
._wrapped
._node
._vmTracer
._vm
.on('beforeMessage', beforeMessageListener)
}
29 changes: 29 additions & 0 deletions packages/contracts-por/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"compilerOptions": {
"resolveJsonModule": true,
"esModuleInterop": true,
"moduleResolution": "node",
"skipLibCheck": true,
"target": "esnext",
"sourceMap": true,
"baseUrl": ".",
"paths": {
"build/*": ["build/*"],
"build": ["build"],
"fixtures/*": ["test/fixtures/*"],
"fixtures": ["test/fixtures"],
"contracts/*": ["build/types/*"],
"contracts": ["build/types", "build/types/factories"],
"utils/*": ["test/utils/*"],
"utils": ["test/utils"],
"config/*": ["test/config/*"],
"config": ["test/config"],
}
},
"include": [
"build",
"contracts",
"test",
"scripts",
]
}
Loading

0 comments on commit dc41baa

Please sign in to comment.