mirror of
https://github.com/th30d4y/OpenLearnX.git
synced 2026-05-27 03:36:32 +00:00
Fix .gitignore: stop tracking ignored files
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
const hre = require('hardhat');
|
||||
|
||||
async function getChainId() {
|
||||
const chainIdHex = await hre.network.provider.send('eth_chainId', []);
|
||||
return new hre.web3.utils.BN(chainIdHex, 'hex');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getChainId,
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
function computeCreate2Address(saltHex, bytecode, deployer) {
|
||||
return web3.utils.toChecksumAddress(
|
||||
`0x${web3.utils
|
||||
.sha3(`0x${['ff', deployer, saltHex, web3.utils.soliditySha3(bytecode)].map(x => x.replace(/0x/, '')).join('')}`)
|
||||
.slice(-40)}`,
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
computeCreate2Address,
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
const { promisify } = require('util');
|
||||
|
||||
const BridgeAMBMock = artifacts.require('BridgeAMBMock');
|
||||
const BridgeArbitrumL1Mock = artifacts.require('BridgeArbitrumL1Mock');
|
||||
const BridgeArbitrumL2Mock = artifacts.require('BridgeArbitrumL2Mock');
|
||||
const BridgeOptimismMock = artifacts.require('BridgeOptimismMock');
|
||||
const BridgePolygonChildMock = artifacts.require('BridgePolygonChildMock');
|
||||
|
||||
class BridgeHelper {
|
||||
static async deploy(type) {
|
||||
return new BridgeHelper(await deployBridge(type));
|
||||
}
|
||||
|
||||
constructor(bridge) {
|
||||
this.bridge = bridge;
|
||||
this.address = bridge.address;
|
||||
}
|
||||
|
||||
call(from, target, selector = undefined, args = []) {
|
||||
return this.bridge.relayAs(
|
||||
target.address || target,
|
||||
selector ? target.contract.methods[selector](...args).encodeABI() : '0x',
|
||||
from,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function deployBridge(type = 'Arbitrum-L2') {
|
||||
switch (type) {
|
||||
case 'AMB':
|
||||
return BridgeAMBMock.new();
|
||||
|
||||
case 'Arbitrum-L1':
|
||||
return BridgeArbitrumL1Mock.new();
|
||||
|
||||
case 'Arbitrum-L2': {
|
||||
const instance = await BridgeArbitrumL2Mock.new();
|
||||
const code = await web3.eth.getCode(instance.address);
|
||||
await promisify(web3.currentProvider.send.bind(web3.currentProvider))({
|
||||
jsonrpc: '2.0',
|
||||
method: 'hardhat_setCode',
|
||||
params: ['0x0000000000000000000000000000000000000064', code],
|
||||
id: new Date().getTime(),
|
||||
});
|
||||
return BridgeArbitrumL2Mock.at('0x0000000000000000000000000000000000000064');
|
||||
}
|
||||
|
||||
case 'Optimism':
|
||||
return BridgeOptimismMock.new();
|
||||
|
||||
case 'Polygon-Child':
|
||||
return BridgePolygonChildMock.new();
|
||||
|
||||
default:
|
||||
throw new Error(`CrossChain: ${type} is not supported`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
BridgeHelper,
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
const { config } = require('hardhat');
|
||||
|
||||
const optimizationsEnabled = config.solidity.compilers.some(c => c.settings.optimizer.enabled);
|
||||
|
||||
/** Revert handler that supports custom errors. */
|
||||
async function expectRevertCustomError(promise, reason) {
|
||||
try {
|
||||
await promise;
|
||||
expect.fail("Expected promise to throw but it didn't");
|
||||
} catch (revert) {
|
||||
if (reason) {
|
||||
if (optimizationsEnabled) {
|
||||
// Optimizations currently mess with Hardhat's decoding of custom errors
|
||||
expect(revert.message).to.include.oneOf([reason, 'unrecognized return data or custom error']);
|
||||
} else {
|
||||
expect(revert.message).to.include(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
expectRevertCustomError,
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
const ethSigUtil = require('eth-sig-util');
|
||||
const keccak256 = require('keccak256');
|
||||
|
||||
const EIP712Domain = [
|
||||
{ name: 'name', type: 'string' },
|
||||
{ name: 'version', type: 'string' },
|
||||
{ name: 'chainId', type: 'uint256' },
|
||||
{ name: 'verifyingContract', type: 'address' },
|
||||
{ name: 'salt', type: 'bytes32' },
|
||||
];
|
||||
|
||||
const Permit = [
|
||||
{ name: 'owner', type: 'address' },
|
||||
{ name: 'spender', type: 'address' },
|
||||
{ name: 'value', type: 'uint256' },
|
||||
{ name: 'nonce', type: 'uint256' },
|
||||
{ name: 'deadline', type: 'uint256' },
|
||||
];
|
||||
|
||||
function bufferToHexString(buffer) {
|
||||
return '0x' + buffer.toString('hex');
|
||||
}
|
||||
|
||||
function hexStringToBuffer(hexstr) {
|
||||
return Buffer.from(hexstr.replace(/^0x/, ''), 'hex');
|
||||
}
|
||||
|
||||
async function getDomain(contract) {
|
||||
const { fields, name, version, chainId, verifyingContract, salt, extensions } = await contract.eip712Domain();
|
||||
|
||||
if (extensions.length > 0) {
|
||||
throw Error('Extensions not implemented');
|
||||
}
|
||||
|
||||
const domain = { name, version, chainId, verifyingContract, salt };
|
||||
for (const [i, { name }] of EIP712Domain.entries()) {
|
||||
if (!(fields & (1 << i))) {
|
||||
delete domain[name];
|
||||
}
|
||||
}
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
function domainType(domain) {
|
||||
return EIP712Domain.filter(({ name }) => domain[name] !== undefined);
|
||||
}
|
||||
|
||||
function domainSeparator(domain) {
|
||||
return bufferToHexString(
|
||||
ethSigUtil.TypedDataUtils.hashStruct('EIP712Domain', domain, { EIP712Domain: domainType(domain) }),
|
||||
);
|
||||
}
|
||||
|
||||
function hashTypedData(domain, structHash) {
|
||||
return bufferToHexString(
|
||||
keccak256(Buffer.concat(['0x1901', domainSeparator(domain), structHash].map(str => hexStringToBuffer(str)))),
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Permit,
|
||||
getDomain,
|
||||
domainType,
|
||||
domainSeparator,
|
||||
hashTypedData,
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
const { BN } = require('@openzeppelin/test-helpers');
|
||||
|
||||
function Enum(...options) {
|
||||
return Object.fromEntries(options.map((key, i) => [key, new BN(i)]));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Enum,
|
||||
ProposalState: Enum('Pending', 'Active', 'Canceled', 'Defeated', 'Succeeded', 'Queued', 'Expired', 'Executed'),
|
||||
VoteType: Enum('Against', 'For', 'Abstain'),
|
||||
Rounding: Enum('Down', 'Up', 'Zero'),
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
const ImplementationLabel = 'eip1967.proxy.implementation';
|
||||
const AdminLabel = 'eip1967.proxy.admin';
|
||||
const BeaconLabel = 'eip1967.proxy.beacon';
|
||||
|
||||
function labelToSlot(label) {
|
||||
return '0x' + web3.utils.toBN(web3.utils.keccak256(label)).subn(1).toString(16);
|
||||
}
|
||||
|
||||
function getSlot(address, slot) {
|
||||
return web3.eth.getStorageAt(
|
||||
web3.utils.isAddress(address) ? address : address.address,
|
||||
web3.utils.isHex(slot) ? slot : labelToSlot(slot),
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ImplementationLabel,
|
||||
AdminLabel,
|
||||
BeaconLabel,
|
||||
ImplementationSlot: labelToSlot(ImplementationLabel),
|
||||
AdminSlot: labelToSlot(AdminLabel),
|
||||
BeaconSlot: labelToSlot(BeaconLabel),
|
||||
getSlot,
|
||||
};
|
||||
@@ -0,0 +1,201 @@
|
||||
const { forward } = require('../helpers/time');
|
||||
|
||||
function zip(...args) {
|
||||
return Array(Math.max(...args.map(array => array.length)))
|
||||
.fill()
|
||||
.map((_, i) => args.map(array => array[i]));
|
||||
}
|
||||
|
||||
function concatHex(...args) {
|
||||
return web3.utils.bytesToHex([].concat(...args.map(h => web3.utils.hexToBytes(h || '0x'))));
|
||||
}
|
||||
|
||||
function concatOpts(args, opts = null) {
|
||||
return opts ? args.concat(opts) : args;
|
||||
}
|
||||
|
||||
class GovernorHelper {
|
||||
constructor(governor, mode = 'blocknumber') {
|
||||
this.governor = governor;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
delegate(delegation = {}, opts = null) {
|
||||
return Promise.all([
|
||||
delegation.token.delegate(delegation.to, { from: delegation.to }),
|
||||
delegation.value && delegation.token.transfer(...concatOpts([delegation.to, delegation.value]), opts),
|
||||
delegation.tokenId &&
|
||||
delegation.token
|
||||
.ownerOf(delegation.tokenId)
|
||||
.then(owner =>
|
||||
delegation.token.transferFrom(...concatOpts([owner, delegation.to, delegation.tokenId], opts)),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
propose(opts = null) {
|
||||
const proposal = this.currentProposal;
|
||||
|
||||
return this.governor.methods[
|
||||
proposal.useCompatibilityInterface
|
||||
? 'propose(address[],uint256[],string[],bytes[],string)'
|
||||
: 'propose(address[],uint256[],bytes[],string)'
|
||||
](...concatOpts(proposal.fullProposal, opts));
|
||||
}
|
||||
|
||||
queue(opts = null) {
|
||||
const proposal = this.currentProposal;
|
||||
|
||||
return proposal.useCompatibilityInterface
|
||||
? this.governor.methods['queue(uint256)'](...concatOpts([proposal.id], opts))
|
||||
: this.governor.methods['queue(address[],uint256[],bytes[],bytes32)'](
|
||||
...concatOpts(proposal.shortProposal, opts),
|
||||
);
|
||||
}
|
||||
|
||||
execute(opts = null) {
|
||||
const proposal = this.currentProposal;
|
||||
|
||||
return proposal.useCompatibilityInterface
|
||||
? this.governor.methods['execute(uint256)'](...concatOpts([proposal.id], opts))
|
||||
: this.governor.methods['execute(address[],uint256[],bytes[],bytes32)'](
|
||||
...concatOpts(proposal.shortProposal, opts),
|
||||
);
|
||||
}
|
||||
|
||||
cancel(visibility = 'external', opts = null) {
|
||||
const proposal = this.currentProposal;
|
||||
|
||||
switch (visibility) {
|
||||
case 'external':
|
||||
if (proposal.useCompatibilityInterface) {
|
||||
return this.governor.methods['cancel(uint256)'](...concatOpts([proposal.id], opts));
|
||||
} else {
|
||||
return this.governor.methods['cancel(address[],uint256[],bytes[],bytes32)'](
|
||||
...concatOpts(proposal.shortProposal, opts),
|
||||
);
|
||||
}
|
||||
case 'internal':
|
||||
return this.governor.methods['$_cancel(address[],uint256[],bytes[],bytes32)'](
|
||||
...concatOpts(proposal.shortProposal, opts),
|
||||
);
|
||||
default:
|
||||
throw new Error(`unsuported visibility "${visibility}"`);
|
||||
}
|
||||
}
|
||||
|
||||
vote(vote = {}, opts = null) {
|
||||
const proposal = this.currentProposal;
|
||||
|
||||
return vote.signature
|
||||
? // if signature, and either params or reason →
|
||||
vote.params || vote.reason
|
||||
? vote
|
||||
.signature(this.governor, {
|
||||
proposalId: proposal.id,
|
||||
support: vote.support,
|
||||
reason: vote.reason || '',
|
||||
params: vote.params || '',
|
||||
})
|
||||
.then(({ v, r, s }) =>
|
||||
this.governor.castVoteWithReasonAndParamsBySig(
|
||||
...concatOpts([proposal.id, vote.support, vote.reason || '', vote.params || '', v, r, s], opts),
|
||||
),
|
||||
)
|
||||
: vote
|
||||
.signature(this.governor, {
|
||||
proposalId: proposal.id,
|
||||
support: vote.support,
|
||||
})
|
||||
.then(({ v, r, s }) =>
|
||||
this.governor.castVoteBySig(...concatOpts([proposal.id, vote.support, v, r, s], opts)),
|
||||
)
|
||||
: vote.params
|
||||
? // otherwise if params
|
||||
this.governor.castVoteWithReasonAndParams(
|
||||
...concatOpts([proposal.id, vote.support, vote.reason || '', vote.params], opts),
|
||||
)
|
||||
: vote.reason
|
||||
? // otherwise if reason
|
||||
this.governor.castVoteWithReason(...concatOpts([proposal.id, vote.support, vote.reason], opts))
|
||||
: this.governor.castVote(...concatOpts([proposal.id, vote.support], opts));
|
||||
}
|
||||
|
||||
async waitForSnapshot(offset = 0) {
|
||||
const proposal = this.currentProposal;
|
||||
const timepoint = await this.governor.proposalSnapshot(proposal.id);
|
||||
return forward[this.mode](timepoint.addn(offset));
|
||||
}
|
||||
|
||||
async waitForDeadline(offset = 0) {
|
||||
const proposal = this.currentProposal;
|
||||
const timepoint = await this.governor.proposalDeadline(proposal.id);
|
||||
return forward[this.mode](timepoint.addn(offset));
|
||||
}
|
||||
|
||||
async waitForEta(offset = 0) {
|
||||
const proposal = this.currentProposal;
|
||||
const timestamp = await this.governor.proposalEta(proposal.id);
|
||||
return forward.timestamp(timestamp.addn(offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a proposal either as
|
||||
* 1) an array of objects [{ target, value, data, signature? }]
|
||||
* 2) an object of arrays { targets: [], values: [], data: [], signatures?: [] }
|
||||
*/
|
||||
setProposal(actions, description) {
|
||||
let targets, values, signatures, data, useCompatibilityInterface;
|
||||
|
||||
if (Array.isArray(actions)) {
|
||||
useCompatibilityInterface = actions.some(a => 'signature' in a);
|
||||
targets = actions.map(a => a.target);
|
||||
values = actions.map(a => a.value || '0');
|
||||
signatures = actions.map(a => a.signature || '');
|
||||
data = actions.map(a => a.data || '0x');
|
||||
} else {
|
||||
useCompatibilityInterface = Array.isArray(actions.signatures);
|
||||
({ targets, values, signatures = [], data } = actions);
|
||||
}
|
||||
|
||||
const fulldata = zip(
|
||||
signatures.map(s => s && web3.eth.abi.encodeFunctionSignature(s)),
|
||||
data,
|
||||
).map(hexs => concatHex(...hexs));
|
||||
|
||||
const descriptionHash = web3.utils.keccak256(description);
|
||||
|
||||
// condensed version for queueing end executing
|
||||
const shortProposal = [targets, values, fulldata, descriptionHash];
|
||||
|
||||
// full version for proposing
|
||||
const fullProposal = [targets, values, ...(useCompatibilityInterface ? [signatures] : []), data, description];
|
||||
|
||||
// proposal id
|
||||
const id = web3.utils.toBN(
|
||||
web3.utils.keccak256(
|
||||
web3.eth.abi.encodeParameters(['address[]', 'uint256[]', 'bytes[]', 'bytes32'], shortProposal),
|
||||
),
|
||||
);
|
||||
|
||||
this.currentProposal = {
|
||||
id,
|
||||
targets,
|
||||
values,
|
||||
signatures,
|
||||
data,
|
||||
fulldata,
|
||||
description,
|
||||
descriptionHash,
|
||||
shortProposal,
|
||||
fullProposal,
|
||||
useCompatibilityInterface,
|
||||
};
|
||||
|
||||
return this.currentProposal;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
GovernorHelper,
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
function mapValues(obj, fn) {
|
||||
return Object.fromEntries([...Object.entries(obj)].map(([k, v]) => [k, fn(v)]));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mapValues,
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
function toEthSignedMessageHash(messageHex) {
|
||||
const messageBuffer = Buffer.from(messageHex.substring(2), 'hex');
|
||||
const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${messageBuffer.length}`);
|
||||
return web3.utils.sha3(Buffer.concat([prefix, messageBuffer]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a signed data with intended validator according to the version 0 of EIP-191
|
||||
* @param validatorAddress The address of the validator
|
||||
* @param dataHex The data to be concatenated with the prefix and signed
|
||||
*/
|
||||
function toDataWithIntendedValidatorHash(validatorAddress, dataHex) {
|
||||
const validatorBuffer = Buffer.from(web3.utils.hexToBytes(validatorAddress));
|
||||
const dataBuffer = Buffer.from(web3.utils.hexToBytes(dataHex));
|
||||
const preambleBuffer = Buffer.from('\x19');
|
||||
const versionBuffer = Buffer.from('\x00');
|
||||
const ethMessage = Buffer.concat([preambleBuffer, versionBuffer, validatorBuffer, dataBuffer]);
|
||||
|
||||
return web3.utils.sha3(ethMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a signer between a contract and a signer for a voucher of method, args, and redeemer
|
||||
* Note that `method` is the web3 method, not the truffle-contract method
|
||||
* @param contract TruffleContract
|
||||
* @param signer address
|
||||
* @param redeemer address
|
||||
* @param methodName string
|
||||
* @param methodArgs any[]
|
||||
*/
|
||||
const getSignFor =
|
||||
(contract, signer) =>
|
||||
(redeemer, methodName, methodArgs = []) => {
|
||||
const parts = [contract.address, redeemer];
|
||||
|
||||
const REAL_SIGNATURE_SIZE = 2 * 65; // 65 bytes in hexadecimal string length
|
||||
const PADDED_SIGNATURE_SIZE = 2 * 96; // 96 bytes in hexadecimal string length
|
||||
const DUMMY_SIGNATURE = `0x${web3.utils.padLeft('', REAL_SIGNATURE_SIZE)}`;
|
||||
|
||||
// if we have a method, add it to the parts that we're signing
|
||||
if (methodName) {
|
||||
if (methodArgs.length > 0) {
|
||||
parts.push(
|
||||
contract.contract.methods[methodName](...methodArgs.concat([DUMMY_SIGNATURE]))
|
||||
.encodeABI()
|
||||
.slice(0, -1 * PADDED_SIGNATURE_SIZE),
|
||||
);
|
||||
} else {
|
||||
const abi = contract.abi.find(abi => abi.name === methodName);
|
||||
parts.push(abi.signature);
|
||||
}
|
||||
}
|
||||
|
||||
// return the signature of the "Ethereum Signed Message" hash of the hash of `parts`
|
||||
const messageHex = web3.utils.soliditySha3(...parts);
|
||||
return web3.eth.sign(messageHex, signer);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
toEthSignedMessageHash,
|
||||
toDataWithIntendedValidatorHash,
|
||||
getSignFor,
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
const ozHelpers = require('@openzeppelin/test-helpers');
|
||||
const helpers = require('@nomicfoundation/hardhat-network-helpers');
|
||||
|
||||
module.exports = {
|
||||
clock: {
|
||||
blocknumber: () => helpers.time.latestBlock(),
|
||||
timestamp: () => helpers.time.latest(),
|
||||
},
|
||||
clockFromReceipt: {
|
||||
blocknumber: receipt => Promise.resolve(receipt.blockNumber),
|
||||
timestamp: receipt => web3.eth.getBlock(receipt.blockNumber).then(block => block.timestamp),
|
||||
},
|
||||
forward: {
|
||||
blocknumber: ozHelpers.time.advanceBlockTo,
|
||||
timestamp: helpers.time.increaseTo,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
const { network } = require('hardhat');
|
||||
const { promisify } = require('util');
|
||||
|
||||
const queue = promisify(setImmediate);
|
||||
|
||||
async function countPendingTransactions() {
|
||||
return parseInt(await network.provider.send('eth_getBlockTransactionCountByNumber', ['pending']));
|
||||
}
|
||||
|
||||
async function batchInBlock(txs) {
|
||||
try {
|
||||
// disable auto-mining
|
||||
await network.provider.send('evm_setAutomine', [false]);
|
||||
// send all transactions
|
||||
const promises = txs.map(fn => fn());
|
||||
// wait for node to have all pending transactions
|
||||
while (txs.length > (await countPendingTransactions())) {
|
||||
await queue();
|
||||
}
|
||||
// mine one block
|
||||
await network.provider.send('evm_mine');
|
||||
// fetch receipts
|
||||
const receipts = await Promise.all(promises);
|
||||
// Sanity check, all tx should be in the same block
|
||||
const minedBlocks = new Set(receipts.map(({ receipt }) => receipt.blockNumber));
|
||||
expect(minedBlocks.size).to.equal(1);
|
||||
|
||||
return receipts;
|
||||
} finally {
|
||||
// enable auto-mining
|
||||
await network.provider.send('evm_setAutomine', [true]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
countPendingTransactions,
|
||||
batchInBlock,
|
||||
};
|
||||
Reference in New Issue
Block a user