mirror of
https://github.com/logos-storage/logos-storage-contracts-eth.git
synced 2026-01-07 15:53:07 +00:00
Move to ethers 6 and use hardhat ignition for deployments
This commit is contained in:
parent
2dddc26015
commit
de58989edf
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@ -10,7 +10,6 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
formatting:
|
formatting:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -29,7 +28,7 @@ jobs:
|
|||||||
# workaround for https://github.com/NomicFoundation/hardhat/issues/3877
|
# workaround for https://github.com/NomicFoundation/hardhat/issues/3877
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18.15
|
node-version: 22
|
||||||
- run: npm install
|
- run: npm install
|
||||||
- run: npm test
|
- run: npm test
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v4
|
||||||
@ -53,9 +52,9 @@ jobs:
|
|||||||
- name: Install Java
|
- name: Install Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: "zulu"
|
||||||
java-version: '11'
|
java-version: "11"
|
||||||
java-package: 'jre'
|
java-package: "jre"
|
||||||
|
|
||||||
- name: Install Certora CLI
|
- name: Install Certora CLI
|
||||||
run: pip3 install certora-cli==7.10.2
|
run: pip3 install certora-cli==7.10.2
|
||||||
@ -88,4 +87,3 @@ jobs:
|
|||||||
rule:
|
rule:
|
||||||
- verify:marketplace
|
- verify:marketplace
|
||||||
- verify:state_changes
|
- verify:state_changes
|
||||||
|
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,3 +4,5 @@ artifacts
|
|||||||
deployment-localhost.json
|
deployment-localhost.json
|
||||||
crytic-export
|
crytic-export
|
||||||
.certora_internal
|
.certora_internal
|
||||||
|
coverage
|
||||||
|
coverage.json
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
require("@nomiclabs/hardhat-waffle")
|
require("@nomicfoundation/hardhat-toolbox")
|
||||||
require("hardhat-deploy")
|
|
||||||
require("hardhat-deploy-ethers")
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
solidity: {
|
solidity: {
|
||||||
|
|||||||
15
ignition/modules/endian.js
Normal file
15
ignition/modules/endian.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
|
||||||
|
module.exports = buildModule("Endian", (m) => {
|
||||||
|
const deployer = m.getAccount(0)
|
||||||
|
|
||||||
|
const endian = m.contract("Endian", [], {
|
||||||
|
from: deployer,
|
||||||
|
})
|
||||||
|
|
||||||
|
const testEndian = m.contract("TestEndian", [], {
|
||||||
|
from: deployer,
|
||||||
|
})
|
||||||
|
|
||||||
|
return { endian, testEndian }
|
||||||
|
})
|
||||||
56
ignition/modules/marketplace.js
Normal file
56
ignition/modules/marketplace.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
const { loadZkeyHash } = require("../../verifier/verifier.js")
|
||||||
|
const { loadConfiguration } = require("../../configuration/configuration.js")
|
||||||
|
const TokenModule = require("./token.js")
|
||||||
|
const VerifierModule = require("./verifier.js")
|
||||||
|
|
||||||
|
function getDefaultConfig() {
|
||||||
|
const zkeyHash = loadZkeyHash(hre.network.name)
|
||||||
|
const config = loadConfiguration(hre.network.name)
|
||||||
|
config.proofs.zkeyHash = zkeyHash
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDefaultVerifier(m) {
|
||||||
|
const { verifier } = m.useModule(VerifierModule)
|
||||||
|
return verifier
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = buildModule("Marketplace", (m) => {
|
||||||
|
const deployer = m.getAccount(0)
|
||||||
|
|
||||||
|
const { token } = m.useModule(TokenModule)
|
||||||
|
const { verifier } = m.useModule(VerifierModule)
|
||||||
|
const configuration = m.getParameter("configuration", getDefaultConfig())
|
||||||
|
|
||||||
|
const marketplace = m.contract(
|
||||||
|
"Marketplace",
|
||||||
|
[configuration, token, verifier],
|
||||||
|
{
|
||||||
|
from: deployer,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let testMarketplace
|
||||||
|
|
||||||
|
if (hre.network.config.tags.includes("local")) {
|
||||||
|
const { testVerifier } = m.useModule(VerifierModule)
|
||||||
|
|
||||||
|
testMarketplace = m.contract(
|
||||||
|
"TestMarketplace",
|
||||||
|
[configuration, token, testVerifier],
|
||||||
|
{
|
||||||
|
from: deployer,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
marketplace,
|
||||||
|
testMarketplace,
|
||||||
|
token,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// // Tags and dependencies
|
||||||
|
// module.exports.tags = ["Marketplace"]
|
||||||
12
ignition/modules/periods.js
Normal file
12
ignition/modules/periods.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
|
||||||
|
module.exports = buildModule("Periods", (m) => {
|
||||||
|
const deployer = m.getAccount(0)
|
||||||
|
const secondsPerPeriod = m.getParameter("secondsPerPeriod", 0)
|
||||||
|
|
||||||
|
const periods = m.contract("Periods", [secondsPerPeriod], {
|
||||||
|
from: deployer,
|
||||||
|
})
|
||||||
|
|
||||||
|
return { periods }
|
||||||
|
})
|
||||||
15
ignition/modules/proofs.js
Normal file
15
ignition/modules/proofs.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
const VerifierModule = require("./verifier.js")
|
||||||
|
|
||||||
|
module.exports = buildModule("Proofs", (m) => {
|
||||||
|
const deployer = m.getAccount(0)
|
||||||
|
|
||||||
|
const { verifier } = m.useModule(VerifierModule)
|
||||||
|
const configuration = m.getParameter("configuration", null)
|
||||||
|
|
||||||
|
const testProofs = m.contract("TestProofs", [configuration, verifier], {
|
||||||
|
from: deployer,
|
||||||
|
})
|
||||||
|
|
||||||
|
return { testProofs }
|
||||||
|
})
|
||||||
17
ignition/modules/slot-reservations.js
Normal file
17
ignition/modules/slot-reservations.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
|
||||||
|
module.exports = buildModule("SlotReservations", (m) => {
|
||||||
|
const deployer = m.getAccount(0)
|
||||||
|
|
||||||
|
const configuration = m.getParameter("configuration", null)
|
||||||
|
|
||||||
|
const testSlotReservations = m.contract(
|
||||||
|
"TestSlotReservations",
|
||||||
|
[configuration],
|
||||||
|
{
|
||||||
|
from: deployer,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return { testSlotReservations }
|
||||||
|
})
|
||||||
22
ignition/modules/token.js
Normal file
22
ignition/modules/token.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
|
||||||
|
const MAX_ACCOUNTS = 20
|
||||||
|
const MINTED_TOKENS = 1_000_000_000_000_000n
|
||||||
|
|
||||||
|
module.exports = buildModule("Token", (m) => {
|
||||||
|
const deployer = m.getAccount(0)
|
||||||
|
|
||||||
|
const token = m.contract("TestToken", [], {
|
||||||
|
from: deployer,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (hre.network.config.tags.includes("local")) {
|
||||||
|
for (let i = 0; i < MAX_ACCOUNTS; i++) {
|
||||||
|
const account = m.getAccount(i)
|
||||||
|
const futureId = "SendingEth" + i
|
||||||
|
m.send(futureId, account, MINTED_TOKENS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { token }
|
||||||
|
})
|
||||||
17
ignition/modules/verifier.js
Normal file
17
ignition/modules/verifier.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
const { loadVerificationKey } = require("../../verifier/verifier.js")
|
||||||
|
|
||||||
|
module.exports = buildModule("Verifier", (m) => {
|
||||||
|
const deployer = m.getAccount(0)
|
||||||
|
|
||||||
|
const verificationKey = loadVerificationKey(hre.network.name)
|
||||||
|
const verifier = m.contract("Groth16Verifier", [verificationKey], {
|
||||||
|
from: deployer,
|
||||||
|
})
|
||||||
|
|
||||||
|
const testVerifier = m.contract("TestVerifier", [], {
|
||||||
|
from: deployer,
|
||||||
|
})
|
||||||
|
|
||||||
|
return { verifier, testVerifier }
|
||||||
|
})
|
||||||
35068
package-lock.json
generated
35068
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@ -2,29 +2,28 @@
|
|||||||
"name": "codex-contracts-eth",
|
"name": "codex-contracts-eth",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "npm run lint && hardhat test",
|
"test": "npm run lint && hardhat test --parallel",
|
||||||
"fuzz": "hardhat compile && fuzzing/fuzz.sh",
|
"fuzz": "hardhat compile && fuzzing/fuzz.sh",
|
||||||
"start": "hardhat node --export deployment-localhost.json",
|
"start": "hardhat node",
|
||||||
"compile": "hardhat compile",
|
"compile": "hardhat compile",
|
||||||
"format": "prettier --write contracts/*.sol contracts/**/*.sol test/**/*.js",
|
"format": "prettier --write contracts/*.sol contracts/**/*.sol test/**/*.js",
|
||||||
"format:check": "prettier --check contracts/*.sol contracts/**/*.sol test/**/*.js",
|
"format:check": "prettier --check contracts/*.sol contracts/**/*.sol test/**/*.js",
|
||||||
"lint": "solhint contracts/**.sol",
|
"lint": "solhint contracts/**.sol",
|
||||||
"deploy": "hardhat deploy",
|
"deploy": "npx hardhat run scripts/deploy.js",
|
||||||
"verify": "npm run verify:marketplace && npm run verify:state_changes",
|
"verify": "npm run verify:marketplace && npm run verify:state_changes",
|
||||||
"verify:marketplace": "certoraRun certora/confs/Marketplace.conf",
|
"verify:marketplace": "certoraRun certora/confs/Marketplace.conf",
|
||||||
"verify:state_changes": "certoraRun certora/confs/StateChanges.conf"
|
"verify:state_changes": "certoraRun certora/confs/StateChanges.conf",
|
||||||
|
"coverage": "npx hardhat coverage",
|
||||||
|
"gas:report": "REPORT_GAS=true npx hardhat test"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nomiclabs/hardhat-ethers": "^2.2.1",
|
|
||||||
"@nomiclabs/hardhat-waffle": "^2.0.3",
|
|
||||||
"@openzeppelin/contracts": "^5.3.0",
|
"@openzeppelin/contracts": "^5.3.0",
|
||||||
"@stdlib/stats-binomial-test": "^0.0.7",
|
"@nomicfoundation/hardhat-chai-matchers": "^2.0.8",
|
||||||
|
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
|
||||||
|
"@stdlib/stats-binomial-test": "^0.2.2",
|
||||||
"chai": "^4.3.7",
|
"chai": "^4.3.7",
|
||||||
"ethereum-waffle": "^3.4.4",
|
"ethers": "6.14.3",
|
||||||
"ethers": "^5.7.2",
|
|
||||||
"hardhat": "^2.24.2",
|
"hardhat": "^2.24.2",
|
||||||
"hardhat-deploy": "^0.11.34",
|
|
||||||
"hardhat-deploy-ethers": "^0.3.0-beta.13",
|
|
||||||
"prettier": "^2.8.2",
|
"prettier": "^2.8.2",
|
||||||
"prettier-plugin-solidity": "^1.4.2",
|
"prettier-plugin-solidity": "^1.4.2",
|
||||||
"solhint": "^5.0.5"
|
"solhint": "^5.0.5"
|
||||||
|
|||||||
24
scripts/deploy.js
Normal file
24
scripts/deploy.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const hre = require("hardhat")
|
||||||
|
const { mine } = require("@nomicfoundation/hardhat-network-helpers")
|
||||||
|
|
||||||
|
const MarketplaceModule = require("../ignition/modules/marketplace")
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
if (hre.network.config.tags.includes("local")) {
|
||||||
|
await mine(256)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { marketplace, testMarketplace } = await hre.ignition.deploy(
|
||||||
|
MarketplaceModule
|
||||||
|
)
|
||||||
|
|
||||||
|
console.info("Deployed Marketplace with Groth16 Verifier at:")
|
||||||
|
console.log(await marketplace.getAddress())
|
||||||
|
|
||||||
|
console.log()
|
||||||
|
|
||||||
|
console.info("Deployed Marketplace with Test Verifier at:")
|
||||||
|
console.log(await testMarketplace.getAddress())
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error)
|
||||||
@ -1,5 +1,5 @@
|
|||||||
const { expect } = require("chai")
|
const { expect } = require("chai")
|
||||||
const { ethers } = require("hardhat")
|
const EndianModule = require("../ignition/modules/endian")
|
||||||
|
|
||||||
describe("Endian", function () {
|
describe("Endian", function () {
|
||||||
const big =
|
const big =
|
||||||
@ -10,8 +10,8 @@ describe("Endian", function () {
|
|||||||
let endian
|
let endian
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
let Endian = await ethers.getContractFactory("TestEndian")
|
const { testEndian } = await ignition.deploy(EndianModule)
|
||||||
endian = await Endian.deploy()
|
endian = testEndian
|
||||||
})
|
})
|
||||||
|
|
||||||
it("converts from little endian to big endian", async function () {
|
it("converts from little endian to big endian", async function () {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,35 @@
|
|||||||
const { expect } = require("chai")
|
const { expect } = require("chai")
|
||||||
const { ethers } = require("hardhat")
|
const PeriodsModule = require("../ignition/modules/periods")
|
||||||
|
|
||||||
describe("Periods", function () {
|
describe("Periods", function () {
|
||||||
it("should revert when secondsPerPeriod is 0", async function () {
|
it("should revert when secondsPerPeriod is 0", async function () {
|
||||||
const PeriodsContract = await ethers.getContractFactory("Periods")
|
const promise = ignition.deploy(PeriodsModule, {
|
||||||
await expect(PeriodsContract.deploy(0)).to.be.revertedWith(
|
parameters: {
|
||||||
"Periods_InvalidSecondsPerPeriod"
|
Periods: {
|
||||||
)
|
secondsPerPeriod: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const expectedError = "Periods_InvalidSecondsPerPeriod"
|
||||||
|
|
||||||
|
const error = await expect(promise).to.be.rejected
|
||||||
|
expect(error)
|
||||||
|
.to.have.property("message")
|
||||||
|
.that.contains(
|
||||||
|
expectedError,
|
||||||
|
`Expected error ${expectedError}, but got ${error.message}`
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should not revert when secondsPerPeriod more than 0", async function () {
|
it("should not revert when secondsPerPeriod more than 0", async function () {
|
||||||
const PeriodsContract = await ethers.getContractFactory("Periods")
|
const promise = ignition.deploy(PeriodsModule, {
|
||||||
await expect(PeriodsContract.deploy(10)).not.to.be.reverted
|
parameters: {
|
||||||
|
Periods: {
|
||||||
|
secondsPerPeriod: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(promise).not.to.be.rejected
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,20 +1,21 @@
|
|||||||
const { expect } = require("chai")
|
const { expect } = require("chai")
|
||||||
const { ethers, deployments } = require("hardhat")
|
const { ethers } = require("hardhat")
|
||||||
const { hexlify, randomBytes } = ethers.utils
|
const { hexlify, randomBytes } = ethers
|
||||||
const {
|
const {
|
||||||
snapshot,
|
snapshot,
|
||||||
revert,
|
revert,
|
||||||
mine,
|
|
||||||
ensureMinimumBlockHeight,
|
ensureMinimumBlockHeight,
|
||||||
currentTime,
|
currentTime,
|
||||||
advanceTime,
|
advanceTime,
|
||||||
advanceTimeTo,
|
advanceTimeTo,
|
||||||
|
mine,
|
||||||
} = require("./evm")
|
} = require("./evm")
|
||||||
const { periodic } = require("./time")
|
const { periodic } = require("./time")
|
||||||
const { loadProof, loadPublicInput } = require("../verifier/verifier")
|
const { loadProof, loadPublicInput } = require("../verifier/verifier")
|
||||||
const { SlotState } = require("./requests")
|
const { SlotState } = require("./requests")
|
||||||
const binomialTest = require("@stdlib/stats-binomial-test")
|
const binomialTest = require("@stdlib/stats-binomial-test")
|
||||||
const { exampleProof } = require("./examples")
|
const { exampleProof } = require("./examples")
|
||||||
|
const ProofsModule = require("../ignition/modules/proofs")
|
||||||
|
|
||||||
describe("Proofs", function () {
|
describe("Proofs", function () {
|
||||||
const slotId = hexlify(randomBytes(32))
|
const slotId = hexlify(randomBytes(32))
|
||||||
@ -30,13 +31,22 @@ describe("Proofs", function () {
|
|||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
await snapshot()
|
await snapshot()
|
||||||
await ensureMinimumBlockHeight(256)
|
await ensureMinimumBlockHeight(256)
|
||||||
const Proofs = await ethers.getContractFactory("TestProofs")
|
|
||||||
await deployments.fixture(["Verifier"])
|
const { testProofs } = await ignition.deploy(ProofsModule, {
|
||||||
const verifier = await deployments.get("Groth16Verifier")
|
parameters: {
|
||||||
proofs = await Proofs.deploy(
|
Proofs: {
|
||||||
{ period, timeout, downtime, zkeyHash: "", downtimeProduct },
|
configuration: {
|
||||||
verifier.address
|
period,
|
||||||
)
|
timeout,
|
||||||
|
downtime,
|
||||||
|
zkeyHash: "",
|
||||||
|
downtimeProduct,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
proofs = testProofs
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async function () {
|
afterEach(async function () {
|
||||||
@ -113,7 +123,7 @@ describe("Proofs", function () {
|
|||||||
let previous = await proofs.getPointer(slotId)
|
let previous = await proofs.getPointer(slotId)
|
||||||
await mine()
|
await mine()
|
||||||
let current = await proofs.getPointer(slotId)
|
let current = await proofs.getPointer(slotId)
|
||||||
expect(current).to.equal((previous + 1) % 256)
|
expect(current).to.equal((previous + 1n) % 256n)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -203,14 +213,14 @@ describe("Proofs", function () {
|
|||||||
let invalid = exampleProof()
|
let invalid = exampleProof()
|
||||||
await expect(
|
await expect(
|
||||||
proofs.proofReceived(slotId, invalid, pubSignals)
|
proofs.proofReceived(slotId, invalid, pubSignals)
|
||||||
).to.be.revertedWith("Proofs_InvalidProof")
|
).to.be.revertedWithCustomError(proofs, "Proofs_InvalidProof")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("fails proof submission when public input is incorrect", async function () {
|
it("fails proof submission when public input is incorrect", async function () {
|
||||||
let invalid = [1, 2, 3]
|
let invalid = [1, 2, 3]
|
||||||
await expect(
|
await expect(
|
||||||
proofs.proofReceived(slotId, proof, invalid)
|
proofs.proofReceived(slotId, proof, invalid)
|
||||||
).to.be.revertedWith("Proofs_InvalidProof")
|
).to.be.revertedWithCustomError(proofs, "Proofs_InvalidProof")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("emits an event when proof was submitted", async function () {
|
it("emits an event when proof was submitted", async function () {
|
||||||
@ -223,7 +233,7 @@ describe("Proofs", function () {
|
|||||||
await proofs.proofReceived(slotId, proof, pubSignals)
|
await proofs.proofReceived(slotId, proof, pubSignals)
|
||||||
await expect(
|
await expect(
|
||||||
proofs.proofReceived(slotId, proof, pubSignals)
|
proofs.proofReceived(slotId, proof, pubSignals)
|
||||||
).to.be.revertedWith("Proofs_ProofAlreadySubmitted")
|
).to.be.revertedWithCustomError(proofs, "Proofs_ProofAlreadySubmitted")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("marks a proof as missing", async function () {
|
it("marks a proof as missing", async function () {
|
||||||
@ -240,7 +250,7 @@ describe("Proofs", function () {
|
|||||||
let currentPeriod = periodOf(await currentTime())
|
let currentPeriod = periodOf(await currentTime())
|
||||||
await expect(
|
await expect(
|
||||||
proofs.markProofAsMissing(slotId, currentPeriod)
|
proofs.markProofAsMissing(slotId, currentPeriod)
|
||||||
).to.be.revertedWith("Proofs_PeriodNotEnded")
|
).to.be.revertedWithCustomError(proofs, "Proofs_PeriodNotEnded")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not mark a proof as missing after timeout", async function () {
|
it("does not mark a proof as missing after timeout", async function () {
|
||||||
@ -249,7 +259,7 @@ describe("Proofs", function () {
|
|||||||
await advanceTimeTo(periodEnd(currentPeriod) + timeout + 1)
|
await advanceTimeTo(periodEnd(currentPeriod) + timeout + 1)
|
||||||
await expect(
|
await expect(
|
||||||
proofs.markProofAsMissing(slotId, currentPeriod)
|
proofs.markProofAsMissing(slotId, currentPeriod)
|
||||||
).to.be.revertedWith("Proofs_ValidationTimedOut")
|
).to.be.revertedWithCustomError(proofs, "Proofs_ValidationTimedOut")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not mark a received proof as missing", async function () {
|
it("does not mark a received proof as missing", async function () {
|
||||||
@ -259,7 +269,7 @@ describe("Proofs", function () {
|
|||||||
await advanceTimeTo(periodEnd(receivedPeriod) + 1)
|
await advanceTimeTo(periodEnd(receivedPeriod) + 1)
|
||||||
await expect(
|
await expect(
|
||||||
proofs.markProofAsMissing(slotId, receivedPeriod)
|
proofs.markProofAsMissing(slotId, receivedPeriod)
|
||||||
).to.be.revertedWith("Proofs_ProofNotMissing")
|
).to.be.revertedWithCustomError(proofs, "Proofs_ProofNotMissing")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not mark proof as missing when not required", async function () {
|
it("does not mark proof as missing when not required", async function () {
|
||||||
@ -270,7 +280,7 @@ describe("Proofs", function () {
|
|||||||
await advanceTimeTo(periodEnd(currentPeriod) + 1)
|
await advanceTimeTo(periodEnd(currentPeriod) + 1)
|
||||||
await expect(
|
await expect(
|
||||||
proofs.markProofAsMissing(slotId, currentPeriod)
|
proofs.markProofAsMissing(slotId, currentPeriod)
|
||||||
).to.be.revertedWith("Proofs_ProofNotRequired")
|
).to.be.revertedWithCustomError(proofs, "Proofs_ProofNotRequired")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not mark proof as missing twice", async function () {
|
it("does not mark proof as missing twice", async function () {
|
||||||
@ -280,7 +290,10 @@ describe("Proofs", function () {
|
|||||||
await proofs.markProofAsMissing(slotId, missedPeriod)
|
await proofs.markProofAsMissing(slotId, missedPeriod)
|
||||||
await expect(
|
await expect(
|
||||||
proofs.markProofAsMissing(slotId, missedPeriod)
|
proofs.markProofAsMissing(slotId, missedPeriod)
|
||||||
).to.be.revertedWith("Proofs_ProofAlreadyMarkedMissing")
|
).to.be.revertedWithCustomError(
|
||||||
|
proofs,
|
||||||
|
"Proofs_ProofAlreadyMarkedMissing"
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("requires no proofs when slot is finished", async function () {
|
it("requires no proofs when slot is finished", async function () {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ const { ethers } = require("hardhat")
|
|||||||
const { exampleRequest, exampleConfiguration } = require("./examples")
|
const { exampleRequest, exampleConfiguration } = require("./examples")
|
||||||
const { requestId, slotId } = require("./ids")
|
const { requestId, slotId } = require("./ids")
|
||||||
const { SlotState } = require("./requests")
|
const { SlotState } = require("./requests")
|
||||||
|
const SlotReservationsModule = require("../ignition/modules/slot-reservations")
|
||||||
|
|
||||||
describe("SlotReservations", function () {
|
describe("SlotReservations", function () {
|
||||||
let reservations
|
let reservations
|
||||||
@ -15,10 +16,18 @@ describe("SlotReservations", function () {
|
|||||||
const config = exampleConfiguration()
|
const config = exampleConfiguration()
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
let SlotReservations = await ethers.getContractFactory(
|
const { testSlotReservations } = await ignition.deploy(
|
||||||
"TestSlotReservations"
|
SlotReservationsModule,
|
||||||
|
{
|
||||||
|
parameters: {
|
||||||
|
SlotReservations: {
|
||||||
|
configuration: config.reservations,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
reservations = await SlotReservations.deploy(config.reservations)
|
|
||||||
|
reservations = testSlotReservations
|
||||||
;[provider, address1, address2, address3] = await ethers.getSigners()
|
;[provider, address1, address2, address3] = await ethers.getSigners()
|
||||||
|
|
||||||
request = await exampleRequest()
|
request = await exampleRequest()
|
||||||
@ -76,7 +85,10 @@ describe("SlotReservations", function () {
|
|||||||
|
|
||||||
it("cannot reserve a slot more than once", async function () {
|
it("cannot reserve a slot more than once", async function () {
|
||||||
await reservations.reserveSlot(reqId, slotIndex)
|
await reservations.reserveSlot(reqId, slotIndex)
|
||||||
await expect(reservations.reserveSlot(reqId, slotIndex)).to.be.revertedWith(
|
await expect(
|
||||||
|
reservations.reserveSlot(reqId, slotIndex)
|
||||||
|
).to.be.revertedWithCustomError(
|
||||||
|
reservations,
|
||||||
"SlotReservations_ReservationNotAllowed"
|
"SlotReservations_ReservationNotAllowed"
|
||||||
)
|
)
|
||||||
expect(await reservations.length(id)).to.equal(1)
|
expect(await reservations.length(id)).to.equal(1)
|
||||||
@ -95,7 +107,10 @@ describe("SlotReservations", function () {
|
|||||||
switchAccount(address3)
|
switchAccount(address3)
|
||||||
await reservations.reserveSlot(reqId, slotIndex)
|
await reservations.reserveSlot(reqId, slotIndex)
|
||||||
switchAccount(provider)
|
switchAccount(provider)
|
||||||
await expect(reservations.reserveSlot(reqId, slotIndex)).to.be.revertedWith(
|
await expect(
|
||||||
|
reservations.reserveSlot(reqId, slotIndex)
|
||||||
|
).to.be.revertedWithCustomError(
|
||||||
|
reservations,
|
||||||
"SlotReservations_ReservationNotAllowed"
|
"SlotReservations_ReservationNotAllowed"
|
||||||
)
|
)
|
||||||
expect(await reservations.length(id)).to.equal(3)
|
expect(await reservations.length(id)).to.equal(3)
|
||||||
@ -115,7 +130,10 @@ describe("SlotReservations", function () {
|
|||||||
|
|
||||||
it("cannot reserve a slot if not free or not in repair", async function () {
|
it("cannot reserve a slot if not free or not in repair", async function () {
|
||||||
await reservations.setSlotState(id, SlotState.Filled)
|
await reservations.setSlotState(id, SlotState.Filled)
|
||||||
await expect(reservations.reserveSlot(reqId, slotIndex)).to.be.revertedWith(
|
await expect(
|
||||||
|
reservations.reserveSlot(reqId, slotIndex)
|
||||||
|
).to.be.revertedWithCustomError(
|
||||||
|
reservations,
|
||||||
"SlotReservations_ReservationNotAllowed"
|
"SlotReservations_ReservationNotAllowed"
|
||||||
)
|
)
|
||||||
expect(await reservations.length(id)).to.equal(0)
|
expect(await reservations.length(id)).to.equal(0)
|
||||||
|
|||||||
59
test/evm.js
59
test/evm.js
@ -1,63 +1,52 @@
|
|||||||
const { ethers } = require("hardhat")
|
const {
|
||||||
|
time,
|
||||||
|
mine,
|
||||||
|
takeSnapshot,
|
||||||
|
setNextBlockTimestamp,
|
||||||
|
} = require("@nomicfoundation/hardhat-network-helpers")
|
||||||
|
|
||||||
let snapshots = []
|
const snapshots = []
|
||||||
|
|
||||||
async function snapshot() {
|
async function snapshot() {
|
||||||
const id = await ethers.provider.send("evm_snapshot")
|
const snapshot = await takeSnapshot()
|
||||||
const time = await currentTime()
|
|
||||||
const automine = await ethers.provider.send("hardhat_getAutomine")
|
const automine = await ethers.provider.send("hardhat_getAutomine")
|
||||||
snapshots.push({ id, time, automine })
|
const time = await currentTime()
|
||||||
|
snapshots.push({ snapshot, automine, time })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function revert() {
|
async function revert() {
|
||||||
const { id, time, automine } = snapshots.pop()
|
const { snapshot, time, automine } = snapshots.pop()
|
||||||
await ethers.provider.send("evm_revert", [id])
|
if (snapshot) {
|
||||||
|
setNextBlockTimestamp(time)
|
||||||
const current = await currentTime()
|
await ethers.provider.send("evm_setAutomine", [automine])
|
||||||
const nextTime = Math.max(time + 1, current + 1)
|
return snapshot.restore()
|
||||||
|
}
|
||||||
await ethers.provider.send("evm_setNextBlockTimestamp", [nextTime])
|
|
||||||
await ethers.provider.send("evm_setAutomine", [automine])
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables or disables Hardhat's automine mode.
|
|
||||||
*
|
|
||||||
* When automine mode is disabled, transactions that revert are silently ignored!
|
|
||||||
*/
|
|
||||||
async function setAutomine(enabled) {
|
|
||||||
await ethers.provider.send("evm_setAutomine", [enabled])
|
|
||||||
}
|
|
||||||
|
|
||||||
async function mine() {
|
|
||||||
await ethers.provider.send("evm_mine")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureMinimumBlockHeight(height) {
|
async function ensureMinimumBlockHeight(height) {
|
||||||
while ((await ethers.provider.getBlockNumber()) < height) {
|
while ((await time.latestBlock()) < height) {
|
||||||
await mine()
|
await mine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setNextBlockTimestamp(timestamp) {
|
||||||
|
return time.setNextBlockTimestamp(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
async function currentTime() {
|
async function currentTime() {
|
||||||
let block = await ethers.provider.getBlock("latest")
|
return time.latest()
|
||||||
return block.timestamp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function advanceTime(seconds) {
|
async function advanceTime(seconds) {
|
||||||
await ethers.provider.send("evm_increaseTime", [seconds])
|
await time.increase(seconds)
|
||||||
await mine()
|
await mine()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function advanceTimeTo(timestamp) {
|
async function advanceTimeTo(timestamp) {
|
||||||
await setNextBlockTimestamp(timestamp)
|
await time.setNextBlockTimestamp(timestamp)
|
||||||
await mine()
|
await mine()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setNextBlockTimestamp(timestamp) {
|
|
||||||
await ethers.provider.send("evm_setNextBlockTimestamp", [timestamp])
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
snapshot,
|
snapshot,
|
||||||
revert,
|
revert,
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
const { ethers } = require("hardhat")
|
|
||||||
const { hours } = require("./time")
|
const { hours } = require("./time")
|
||||||
const { hexlify, randomBytes } = ethers.utils
|
const { hexlify, randomBytes } = require("hardhat").ethers
|
||||||
|
|
||||||
const exampleConfiguration = () => ({
|
const exampleConfiguration = () => ({
|
||||||
collateral: {
|
collateral: {
|
||||||
@ -36,7 +35,7 @@ const exampleRequest = async () => {
|
|||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
cid: Buffer.from("zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob"),
|
cid: Buffer.from("zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob"),
|
||||||
merkleRoot: Array.from(randomBytes(32)),
|
merkleRoot: randomBytes(32),
|
||||||
},
|
},
|
||||||
expiry: hours(1),
|
expiry: hours(1),
|
||||||
nonce: hexlify(randomBytes(32)),
|
nonce: hexlify(randomBytes(32)),
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
const { ethers } = require("hardhat")
|
const { keccak256, AbiCoder } = require("hardhat").ethers
|
||||||
const { keccak256, defaultAbiCoder } = ethers.utils
|
|
||||||
|
|
||||||
function requestId(request) {
|
function requestId(request) {
|
||||||
const Ask = "tuple(uint256, uint256, uint256, uint64, uint64, uint64, int64)"
|
const Ask = "tuple(uint256, uint256, uint256, uint64, uint64, uint64, int64)"
|
||||||
const Content = "tuple(bytes, bytes32)"
|
const Content = "tuple(bytes, bytes32)"
|
||||||
const Request =
|
const Request =
|
||||||
"tuple(address, " + Ask + ", " + Content + ", uint64, bytes32)"
|
"tuple(address, " + Ask + ", " + Content + ", uint64, bytes32)"
|
||||||
return keccak256(defaultAbiCoder.encode([Request], requestToArray(request)))
|
return keccak256(new AbiCoder().encode([Request], requestToArray(request)))
|
||||||
}
|
}
|
||||||
|
|
||||||
function askToArray(ask) {
|
function askToArray(ask) {
|
||||||
@ -40,7 +39,7 @@ function requestToArray(request) {
|
|||||||
function slotId(slot) {
|
function slotId(slot) {
|
||||||
const types = "tuple(bytes32, uint256)"
|
const types = "tuple(bytes32, uint256)"
|
||||||
const values = [slot.request, slot.index]
|
const values = [slot.request, slot.index]
|
||||||
const encoding = defaultAbiCoder.encode([types], [values])
|
const encoding = new AbiCoder().encode([types], [values])
|
||||||
return keccak256(encoding)
|
return keccak256(encoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,16 +4,16 @@ const { payoutForDuration } = require("./price")
|
|||||||
const { collateralPerSlot } = require("./collateral")
|
const { collateralPerSlot } = require("./collateral")
|
||||||
|
|
||||||
async function waitUntilCancelled(contract, request) {
|
async function waitUntilCancelled(contract, request) {
|
||||||
const expiry = (await contract.requestExpiry(requestId(request))).toNumber()
|
const expiry = await contract.requestExpiry(requestId(request))
|
||||||
// We do +1, because the expiry check in contract is done as `>` and not `>=`.
|
// We do +1, because the expiry check in contract is done as `>` and not `>=`.
|
||||||
await advanceTimeTo(expiry + 1)
|
return advanceTimeTo(expiry + 1n)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitUntilSlotsFilled(contract, request, proof, token, slots) {
|
async function waitUntilSlotsFilled(contract, request, proof, token, slots) {
|
||||||
let collateral = collateralPerSlot(request)
|
let collateral = collateralPerSlot(request)
|
||||||
await token.approve(contract.address, collateral * slots.length)
|
await token.approve(await contract.getAddress(), collateral * slots.length)
|
||||||
|
|
||||||
let requestEnd = (await contract.requestEnd(requestId(request))).toNumber()
|
let requestEnd = await contract.requestEnd(requestId(request))
|
||||||
const payouts = []
|
const payouts = []
|
||||||
for (let slotIndex of slots) {
|
for (let slotIndex of slots) {
|
||||||
await contract.reserveSlot(requestId(request), slotIndex)
|
await contract.reserveSlot(requestId(request), slotIndex)
|
||||||
@ -40,9 +40,9 @@ async function waitUntilStarted(contract, request, proof, token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function waitUntilFinished(contract, requestId) {
|
async function waitUntilFinished(contract, requestId) {
|
||||||
const end = (await contract.requestEnd(requestId)).toNumber()
|
const end = await contract.requestEnd(requestId)
|
||||||
// We do +1, because the end check in contract is done as `>` and not `>=`.
|
// We do +1, because the end check in contract is done as `>` and not `>=`.
|
||||||
await advanceTimeTo(end + 1)
|
await advanceTimeTo(end + 1n)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitUntilFailed(contract, request) {
|
async function waitUntilFailed(contract, request) {
|
||||||
@ -96,6 +96,11 @@ function patchOverloads(contract) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function littleEndianToBigInt(littleEndian) {
|
||||||
|
const buffer = Buffer.from(littleEndian)
|
||||||
|
return BigInt(`0x${buffer.toString("hex")}`)
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
waitUntilCancelled,
|
waitUntilCancelled,
|
||||||
waitUntilStarted,
|
waitUntilStarted,
|
||||||
@ -104,4 +109,5 @@ module.exports = {
|
|||||||
waitUntilFailed,
|
waitUntilFailed,
|
||||||
waitUntilSlotFailed,
|
waitUntilSlotFailed,
|
||||||
patchOverloads,
|
patchOverloads,
|
||||||
|
littleEndianToBigInt,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,21 @@ function maxPrice(request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function payoutForDuration(request, start, end) {
|
function payoutForDuration(request, start, end) {
|
||||||
return (end - start) * pricePerSlotPerSecond(request)
|
return (Number(end) - Number(start)) * pricePerSlotPerSecond(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { maxPrice, pricePerSlotPerSecond, payoutForDuration }
|
function calculatePartialPayout(request, expiresAt, filledAt) {
|
||||||
|
return (Number(expiresAt) - Number(filledAt)) * pricePerSlotPerSecond(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateBalance(balance, reward) {
|
||||||
|
return BigInt(balance) + BigInt(reward)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
maxPrice,
|
||||||
|
pricePerSlotPerSecond,
|
||||||
|
payoutForDuration,
|
||||||
|
calculatePartialPayout,
|
||||||
|
calculateBalance,
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
const { Assertion } = require("chai")
|
const { Assertion } = require("chai")
|
||||||
const { currentTime } = require("./evm")
|
|
||||||
|
|
||||||
const RequestState = {
|
const RequestState = {
|
||||||
New: 0,
|
New: 0,
|
||||||
@ -10,13 +9,13 @@ const RequestState = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SlotState = {
|
const SlotState = {
|
||||||
Free: 0,
|
Free: 0n,
|
||||||
Filled: 1,
|
Filled: 1n,
|
||||||
Finished: 2,
|
Finished: 2n,
|
||||||
Failed: 3,
|
Failed: 3n,
|
||||||
Paid: 4,
|
Paid: 4n,
|
||||||
Cancelled: 5,
|
Cancelled: 5n,
|
||||||
Repair: 6,
|
Repair: 6n,
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableRequestAssertions() {
|
function enableRequestAssertions() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user