Skip to content

feat(lazer/contracts/evm): Add fees for verification #2163

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

Merged
merged 13 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion lazer/contracts/evm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ $ forge snapshot

### Anvil

Anvil does not come with CreateX by default. It can be deployed or an RPC which has the contract can be forked. The below command forks an RPC with a functional CreateX contract.

```shell
$ anvil
$ anvil --fork-url "https://eth-sepolia.public.blastapi.io"
```

### Deploy
Expand All @@ -42,6 +44,15 @@ $ anvil
$ forge script script/PythLazerDeploy.s.sol --rpc-url <your_rpc_url> --private-key <your_private_key> --broadcast
```

### Upgrade

The UUPSUpgradeable feature adds functions to the cocntract which support upgrading through the use of an UUPS/ERC1967Proxy. A function can be defined to migrate state if needed. Be careful of changing storage slots when upgrading. See [Documentation](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable) for more details.
In addition, the private key is necessary or contracts will be deployed to different addresses than expected.

```shell
$ forge script script/PythLazerDeploy.s.sol --rpc-url <your_rpc_url> --private-key <your_private_key> --broadcast --sig "migrate()"
```

### Cast

```shell
Expand Down
18 changes: 18 additions & 0 deletions lazer/contracts/evm/script/PythLazerDeploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {PythLazer} from "../src/PythLazer.sol";
import {ICreateX} from "createx/ICreateX.sol";
import {CreateX} from "createx/CreateX.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";

// This script deploys the PythLazer proxy and implementation contract using
// CreateX's contract factory to a deterministic address. Having deterministic
Expand Down Expand Up @@ -168,8 +169,25 @@ contract PythLazerDeployScript is Script {
return addr;
}

function getProxyAddress(bytes11 seed) public view returns (address addr) {
(, bytes32 guardedSalt) = generateSalt(seed);
address proxyAddr = createX.computeCreate3Address({salt: guardedSalt});
return proxyAddr;
}

function run() public {
address impl = deployImplementation("lazer:impl");
deployProxy("lazer:proxy", impl);
}

function migrate() public {
// Deploys new version and updates proxy to use new address
address proxyAddress = getProxyAddress("lazer:proxy");
address newImpl = deployImplementation("lazer:impl");
bytes memory migrateCall = abi.encodeWithSignature("migrate()");
vm.startBroadcast();
UUPSUpgradeable proxy = UUPSUpgradeable(proxyAddress);
proxy.upgradeToAndCall(newImpl, migrateCall);
vm.stopBroadcast();
}
}
19 changes: 16 additions & 3 deletions lazer/contracts/evm/src/PythLazer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract PythLazer is OwnableUpgradeable, UUPSUpgradeable {
TrustedSignerInfo[2] public trustedSigners;
TrustedSignerInfo[100] internal trustedSigners;
uint256 public verification_fee;

struct TrustedSignerInfo {
address pubkey;
Expand All @@ -15,6 +16,12 @@ contract PythLazer is OwnableUpgradeable, UUPSUpgradeable {
function initialize(address _topAuthority) public initializer {
__Ownable_init(_topAuthority);
__UUPSUpgradeable_init();

verification_fee = 1 wei;
}

function migrate() public onlyOwner {
verification_fee = 1 wei;
}

function _authorizeUpgrade(address) internal override onlyOwner {}
Expand Down Expand Up @@ -62,7 +69,13 @@ contract PythLazer is OwnableUpgradeable, UUPSUpgradeable {

function verifyUpdate(
bytes calldata update
) external view returns (bytes calldata payload, address signer) {
) external payable returns (bytes calldata payload, address signer) {
// Require fee and refund excess
require(msg.value >= verification_fee, "Insufficient fee provided");
if (msg.value > verification_fee) {
payable(msg.sender).transfer(msg.value - verification_fee);
}

if (update.length < 71) {
revert("input too short");
}
Expand Down Expand Up @@ -93,6 +106,6 @@ contract PythLazer is OwnableUpgradeable, UUPSUpgradeable {
}

function version() public pure returns (string memory) {
return "0.1.0";
return "0.1.1";
}
}
45 changes: 44 additions & 1 deletion lazer/contracts/evm/test/PythLazer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,55 @@ contract PythLazerTest is Test {
pythLazer.initialize(address(1));
}

function test_update() public {
function test_update_add_signer() public {
assert(!pythLazer.isValidSigner(address(2)));
vm.prank(address(1));
pythLazer.updateTrustedSigner(address(2), block.timestamp + 1000);
assert(pythLazer.isValidSigner(address(2)));
skip(2000);
assert(!pythLazer.isValidSigner(address(2)));
}

function test_update_remove_signer() public {
assert(!pythLazer.isValidSigner(address(2)));
vm.prank(address(1));
pythLazer.updateTrustedSigner(address(2), block.timestamp + 1000);
assert(pythLazer.isValidSigner(address(2)));

vm.prank(address(1));
pythLazer.updateTrustedSigner(address(2), 0);
assert(!pythLazer.isValidSigner(address(2)));
}

function test_verify() public {
// Prepare dummy update and signer
address trustedSigner = 0xEfEf56cD66896f6799A90A4e4d512C330c094e44;
vm.prank(address(1));
pythLazer.updateTrustedSigner(trustedSigner, 3000000000000000);
bytes
memory update = hex"2a22999a577d3cc0202197939d736bc0dcf71b9dde7b9470e4d16fa8e2120c0787a1c0d744d0c39cc372af4d1ecf2d09e84160ca905f3f597d20e2eec144a446a0459ad600001c93c7d3750006240af373971c01010000000201000000000005f5e100";

uint256 fee = pythLazer.verification_fee();

address alice = makeAddr("alice");
vm.deal(alice, 1 ether);
address bob = makeAddr("bob");
vm.deal(bob, 1 ether);

// Alice provides appropriate fee
vm.prank(alice);
pythLazer.verifyUpdate{value: fee}(update);
assertEq(alice.balance, 1 ether - fee);

// Alice overpays and is refunded
vm.prank(alice);
pythLazer.verifyUpdate{value: 0.5 ether}(update);
assertEq(alice.balance, 1 ether - fee - fee);

// Bob does not attach a fee
vm.prank(bob);
vm.expectRevert("Insufficient fee provided");
pythLazer.verifyUpdate(update);
assertEq(bob.balance, 1 ether);
}
}
Loading