-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Protocol testnet #16
Protocol testnet #16
Conversation
describe('returns', function () { | ||
let testMemoryUtils: Awaited<ReturnType<typeof deployTestMemoryUtils>>['testMemoryUtilsContract']; | ||
|
||
beforeAll(async () => { | ||
testMemoryUtils = (await deployTestMemoryUtils()).testMemoryUtilsContract; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test suite is critical to ensure future updates do not change memory locations.
It ensures we don't lock ourselves out of an admin slot for example
@@ -7,17 +7,21 @@ import { generateWallets } from './generateWallets'; | |||
export async function deployBuilderNftContract({ USDCContractAddress }: { USDCContractAddress?: Address } = {}) { | |||
const { adminAccount: admin, thirdUserAccount: proceedsReceiverAccount } = await generateWallets(); | |||
|
|||
const implementation = await viem.deployContract('BuilderNFTSeasonOneImplementation01'); | |||
const implementation = await viem.deployContract('BuilderNFTSeasonOneImplementation01', [], { | |||
client: { wallet: admin } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed deploys to use a specific wallet as the client, and not use default viem data
import type { GeneratedWallet } from './generateWallets'; | ||
import { generateWallets } from './generateWallets'; | ||
|
||
export async function deployEASContracts() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generates EAS Resolver, and deploys both our schemas
|
||
const decimalMultiplier = 10n ** decimals; | ||
|
||
async function mintTo({ account, amount }: { account: string; amount: number }) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Utility methods for using the ERC20 more easily
initialEthBalance?: number; | ||
}; | ||
|
||
export async function generateWallets({ initialEthBalance = 1 }: GeneratedWalletConfig = { initialEthBalance: 1 }) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I discovered we weren't initialising contracts with wallets, but the default wallet with viem, which is why it automagically worked.
this fixes generate wallets so we for sure always get different wallets
@@ -25,9 +25,9 @@ import { IERC1271 } from "../interface/IERC1271.sol"; | |||
* @dev Signature verification helper that can be used instead of `ECRecover.recover` to seamlessly support both ECDSA | |||
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets. | |||
* | |||
* Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/SignatureChecker.sol | |||
* Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/21bb89ef5bfc789b9333eb05e3ba2b7b284ac77c/contracts/utils/cryptography/SignatureCheckerFiatProxy.sol |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a conflict with another SignatureChecker from EAS libs, so renamed this to avoid issues (This one compiles with 0.6.x whereas EAS compiles with 0.8.27)
|
||
import "@openzeppelin/contracts/utils/StorageSlot.sol"; | ||
|
||
library MemoryUtils { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a single core memory library with namespacing. This makes it easier to reason about every storage slot used across our contracts
import "../libs/ProtocolAccessControl.sol"; | ||
import "../libs/MemoryUtils.sol"; | ||
|
||
contract TestMemoryUtils { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Memory Utils has everything internal
This allows us to access those internals and make them testable
function _setRole(bytes32 role, address account) internal { | ||
require(account != address(0), "Invalid account. Cannot be empty"); | ||
|
||
address _currentHolder = _roleHolder(role); | ||
|
||
// Update the role only if it's different | ||
if (_currentHolder != account) { | ||
MemoryUtils._setAddress(role, account); | ||
emit RoleTransferred(MemoryUtils._getRoleName(role), _currentHolder, account); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Standard behaviour for granting roles.
RoleTransferred provides inbuilt auditability for all our role changes
scoutgameEASResolver?: Address | null; | ||
scoutgameErc20TokenDev?: Address | null; | ||
scoutgameScoutProtocolProxyDev?: Address | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the 3 main keys
I've been considering refactoring these under a dev | prod key with same type to avoid duplication of keys
@@ -1,41 +1,48 @@ | |||
import dotenv from 'dotenv'; | |||
import { execSync } from 'node:child_process'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ESLint across this file
scripts/deploy/deployEASSchemas.ts
Outdated
const easRegistryContract = await viem.getContractAt('ISchemaRegistry', easSchemaRegistryAddress); | ||
|
||
console.log('Deploying the resolver contract...'); | ||
const deployedResolver = await viem.deployContract( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cleaned up deploy script, viem has inbuilt encoding of deploy data and resolution of the ABI
No more need to path.resolve the ABI
process.env.PRIVATE_KEY?.startsWith('0x') ? process.env.PRIVATE_KEY : `0x${process.env.PRIVATE_KEY}` | ||
) as `0x${string}`; | ||
|
||
task('deployScoutGameERC20', 'Deploys or updates the BuilderNFTSeasonOne contracts').setAction( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: We should add an initialise() method or something similar in the ERC20 so that we don't have ability to mint new tokens from thin air
process.env.PRIVATE_KEY?.startsWith('0x') ? process.env.PRIVATE_KEY : `0x${process.env.PRIVATE_KEY}` | ||
) as `0x${string}`; | ||
|
||
task('deployScoutProtocol', 'Deploys or updates the ScoutProtocol contracts').setAction(async (taskArgs, hre) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as buildernft
Deploy a new implementation
Point a proxy to it, or also deploy a new proxy
import fs from 'fs'; | ||
import path from 'path'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ESLint
return true; | ||
} | ||
|
||
function setAttesterWallet(address _attesterWallet) external onlyAdmin { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Role slots defined in Memory utils
Setters for roles other than admin or pauser defined in Contract that uses this role
} | ||
|
||
function setAttesterWallet(address _attesterWallet) external onlyAdmin { | ||
_setRole(MemoryUtils.EAS_ATTESTER_SLOT, _attesterWallet); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is all we need to do.
EASResolver simply asserts access controls on the role assignment
We can then benefit from the standard method which has inbuilt event emissions
|
||
// Allow the sender to claim their balance as ERC20 tokens | ||
function claim( | ||
string memory week, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep the protocol simple by only giving it the notion of weeks
Seasons are managed by the caller
} | ||
|
||
// Function to set the Merkle root for a given week | ||
function setMerkleRoot(string memory week, bytes32 merkleRoot) external onlyAdminOrClaimManager onlyWhenNotPaused { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: We'll want to implement the claiming mechanism towards the vesting contract
We could do that here, but it then attaches this method and prevents us re-running manually
I suggest a claimScoutAllocation method defined separately
|
||
// Function to get the Merkle root hash for a given week | ||
function getMerkleRoot(string memory week) public view returns (bytes32) { | ||
bytes32 slot = keccak256(abi.encodePacked(week, MemoryUtils.MERKLE_ROOTS_SLOT)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably refactor this to a getWeekSlot() method
Also, need to double check how many slots are available in Solidity so we don't hit that max a few years down the line
import "./libs/ProtocolAccessControl.sol"; | ||
import "./libs/MemoryUtils.sol"; | ||
|
||
contract ScoutProtocolProxy is Context, ProtocolAccessControl { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super lightweight proxy
Attestation calldata attestation, | ||
uint256 /*value*/ | ||
) internal override returns (bool) { | ||
require(attestation.attester == attester() || attestation.attester == secondaryAttester(), "Invalid attester"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super lightweight validation on the attester only.
Since we control the attester, we can handle validation before it attest
ScoutTokenERC20 token = _getToken(); | ||
|
||
// Transfer tokens to the user | ||
token.transfer(msg.sender, amount * (10 ** token.decimals())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For easier reasoning, we pass the amount as integers across systems
We only need to wei-ify amounts in the contract
No description provided.