Skip to content

Commit

Permalink
Merge pull request #2163 from pyth-network/add-fees-to-lazer-evm-cont…
Browse files Browse the repository at this point in the history
…ract

feat(lazer/contracts/evm): Add fees for verification
  • Loading branch information
darunrs authored Dec 9, 2024
2 parents 26236b8 + 28aa2c2 commit ba3b04f
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 5 deletions.
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);
}
}

0 comments on commit ba3b04f

Please sign in to comment.