Skip to content

Commit ba3b04f

Browse files
authored
Merge pull request #2163 from pyth-network/add-fees-to-lazer-evm-contract
feat(lazer/contracts/evm): Add fees for verification
2 parents 26236b8 + 28aa2c2 commit ba3b04f

File tree

4 files changed

+90
-5
lines changed

4 files changed

+90
-5
lines changed

lazer/contracts/evm/README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ $ forge snapshot
3232

3333
### Anvil
3434

35+
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.
36+
3537
```shell
36-
$ anvil
38+
$ anvil --fork-url "https://eth-sepolia.public.blastapi.io"
3739
```
3840

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

47+
### Upgrade
48+
49+
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.
50+
In addition, the private key is necessary or contracts will be deployed to different addresses than expected.
51+
52+
```shell
53+
$ forge script script/PythLazerDeploy.s.sol --rpc-url <your_rpc_url> --private-key <your_private_key> --broadcast --sig "migrate()"
54+
```
55+
4556
### Cast
4657

4758
```shell

lazer/contracts/evm/script/PythLazerDeploy.s.sol

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {PythLazer} from "../src/PythLazer.sol";
66
import {ICreateX} from "createx/ICreateX.sol";
77
import {CreateX} from "createx/CreateX.sol";
88
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
9+
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
910

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

172+
function getProxyAddress(bytes11 seed) public view returns (address addr) {
173+
(, bytes32 guardedSalt) = generateSalt(seed);
174+
address proxyAddr = createX.computeCreate3Address({salt: guardedSalt});
175+
return proxyAddr;
176+
}
177+
171178
function run() public {
172179
address impl = deployImplementation("lazer:impl");
173180
deployProxy("lazer:proxy", impl);
174181
}
182+
183+
function migrate() public {
184+
// Deploys new version and updates proxy to use new address
185+
address proxyAddress = getProxyAddress("lazer:proxy");
186+
address newImpl = deployImplementation("lazer:impl");
187+
bytes memory migrateCall = abi.encodeWithSignature("migrate()");
188+
vm.startBroadcast();
189+
UUPSUpgradeable proxy = UUPSUpgradeable(proxyAddress);
190+
proxy.upgradeToAndCall(newImpl, migrateCall);
191+
vm.stopBroadcast();
192+
}
175193
}

lazer/contracts/evm/src/PythLazer.sol

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
55
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
66

77
contract PythLazer is OwnableUpgradeable, UUPSUpgradeable {
8-
TrustedSignerInfo[2] public trustedSigners;
8+
TrustedSignerInfo[100] internal trustedSigners;
9+
uint256 public verification_fee;
910

1011
struct TrustedSignerInfo {
1112
address pubkey;
@@ -15,6 +16,12 @@ contract PythLazer is OwnableUpgradeable, UUPSUpgradeable {
1516
function initialize(address _topAuthority) public initializer {
1617
__Ownable_init(_topAuthority);
1718
__UUPSUpgradeable_init();
19+
20+
verification_fee = 1 wei;
21+
}
22+
23+
function migrate() public onlyOwner {
24+
verification_fee = 1 wei;
1825
}
1926

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

6370
function verifyUpdate(
6471
bytes calldata update
65-
) external view returns (bytes calldata payload, address signer) {
72+
) external payable returns (bytes calldata payload, address signer) {
73+
// Require fee and refund excess
74+
require(msg.value >= verification_fee, "Insufficient fee provided");
75+
if (msg.value > verification_fee) {
76+
payable(msg.sender).transfer(msg.value - verification_fee);
77+
}
78+
6679
if (update.length < 71) {
6780
revert("input too short");
6881
}
@@ -93,6 +106,6 @@ contract PythLazer is OwnableUpgradeable, UUPSUpgradeable {
93106
}
94107

95108
function version() public pure returns (string memory) {
96-
return "0.1.0";
109+
return "0.1.1";
97110
}
98111
}

lazer/contracts/evm/test/PythLazer.t.sol

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,55 @@ contract PythLazerTest is Test {
1212
pythLazer.initialize(address(1));
1313
}
1414

15-
function test_update() public {
15+
function test_update_add_signer() public {
1616
assert(!pythLazer.isValidSigner(address(2)));
1717
vm.prank(address(1));
1818
pythLazer.updateTrustedSigner(address(2), block.timestamp + 1000);
1919
assert(pythLazer.isValidSigner(address(2)));
2020
skip(2000);
2121
assert(!pythLazer.isValidSigner(address(2)));
2222
}
23+
24+
function test_update_remove_signer() public {
25+
assert(!pythLazer.isValidSigner(address(2)));
26+
vm.prank(address(1));
27+
pythLazer.updateTrustedSigner(address(2), block.timestamp + 1000);
28+
assert(pythLazer.isValidSigner(address(2)));
29+
30+
vm.prank(address(1));
31+
pythLazer.updateTrustedSigner(address(2), 0);
32+
assert(!pythLazer.isValidSigner(address(2)));
33+
}
34+
35+
function test_verify() public {
36+
// Prepare dummy update and signer
37+
address trustedSigner = 0xEfEf56cD66896f6799A90A4e4d512C330c094e44;
38+
vm.prank(address(1));
39+
pythLazer.updateTrustedSigner(trustedSigner, 3000000000000000);
40+
bytes
41+
memory update = hex"2a22999a577d3cc0202197939d736bc0dcf71b9dde7b9470e4d16fa8e2120c0787a1c0d744d0c39cc372af4d1ecf2d09e84160ca905f3f597d20e2eec144a446a0459ad600001c93c7d3750006240af373971c01010000000201000000000005f5e100";
42+
43+
uint256 fee = pythLazer.verification_fee();
44+
45+
address alice = makeAddr("alice");
46+
vm.deal(alice, 1 ether);
47+
address bob = makeAddr("bob");
48+
vm.deal(bob, 1 ether);
49+
50+
// Alice provides appropriate fee
51+
vm.prank(alice);
52+
pythLazer.verifyUpdate{value: fee}(update);
53+
assertEq(alice.balance, 1 ether - fee);
54+
55+
// Alice overpays and is refunded
56+
vm.prank(alice);
57+
pythLazer.verifyUpdate{value: 0.5 ether}(update);
58+
assertEq(alice.balance, 1 ether - fee - fee);
59+
60+
// Bob does not attach a fee
61+
vm.prank(bob);
62+
vm.expectRevert("Insufficient fee provided");
63+
pythLazer.verifyUpdate(update);
64+
assertEq(bob.balance, 1 ether);
65+
}
2366
}

0 commit comments

Comments
 (0)