From ac3fbcc033644e0ec257320b0b85170d074220c3 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 25 Sep 2024 18:54:41 -0400 Subject: [PATCH] added proxy --- .gitignore | 2 + .../421614/run-1724978654.json | 100 ------------ .../SimpleOracle.s.sol/421614/run-latest.json | 100 ------------ foundry.toml | 4 + report.md | 90 ++++++---- script/EdgePushOracle.s.sol | 34 ++-- src/EdgePushOracle.sol | 130 ++++++++++----- test/EdgePushOracle.t.sol | 154 ++++++++++++++---- 8 files changed, 291 insertions(+), 323 deletions(-) delete mode 100644 broadcast/SimpleOracle.s.sol/421614/run-1724978654.json delete mode 100644 broadcast/SimpleOracle.s.sol/421614/run-latest.json diff --git a/.gitignore b/.gitignore index 9d91b78..ce8ad58 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ docs/ # Dotenv file .env + +report.md \ No newline at end of file diff --git a/broadcast/SimpleOracle.s.sol/421614/run-1724978654.json b/broadcast/SimpleOracle.s.sol/421614/run-1724978654.json deleted file mode 100644 index 98194e6..0000000 --- a/broadcast/SimpleOracle.s.sol/421614/run-1724978654.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "transactions": [ - { - "hash": "0x67f99e24fb42c52825fc498a4ec6c0957e75816c7a8a7b1f3fbfeebb88ca754b", - "transactionType": "CREATE", - "contractName": "SimpleOracle", - "contractAddress": "0x703dbc7c0ab7f69565fa459b55ff7e1123fd556a", - "function": null, - "arguments": [ - "18", - "test", - "0xc26d7EF337e01a5cC5498D3cc2ff0610761ae637" - ], - "transaction": { - "from": "0xc26d7ef337e01a5cc5498d3cc2ff0610761ae637", - "gas": "0x12ed14", - "value": "0x0", - "input": "0x608060405234801561001057600080fd5b5060405161100438038061100483398101604081905261002f916101da565b806001600160a01b03811661005e57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b610067816100c3565b506003805460ff191660ff851617905560046100838382610358565b5061008f600082610113565b506100ba7f68e79a7bf1e0bc45d0a330c573bc367f9cf464fd326078812f301165fbda4ef182610113565b50505050610417565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008281526001602090815260408083206001600160a01b038516845290915281205460ff1661019e5760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45060016101a2565b5060005b92915050565b634e487b7160e01b600052604160045260246000fd5b80516001600160a01b03811681146101d557600080fd5b919050565b6000806000606084860312156101ef57600080fd5b835160ff8116811461020057600080fd5b602085810151919450906001600160401b038082111561021f57600080fd5b818701915087601f83011261023357600080fd5b815181811115610245576102456101a8565b604051601f8201601f19908116603f0116810190838211818310171561026d5761026d6101a8565b816040528281528a8684870101111561028557600080fd5b600093505b828410156102a7578484018601518185018701529285019261028a565b60008684830101528097505050505050506102c4604085016101be565b90509250925092565b600181811c908216806102e157607f821691505b60208210810361030157634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115610353576000816000526020600020601f850160051c810160208610156103305750805b601f850160051c820191505b8181101561034f5782815560010161033c565b5050505b505050565b81516001600160401b03811115610371576103716101a8565b6103858161037f84546102cd565b84610307565b602080601f8311600181146103ba57600084156103a25750858301515b600019600386901b1c1916600185901b17855561034f565b600085815260208120601f198616915b828110156103e9578886015182559484019460019091019084016103ca565b50858210156104075787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b610bde806104266000396000f3fe608060405234801561001057600080fd5b506004361061014d5760003560e01c80637a1395aa116100c3578063a217fddf1161007c578063a217fddf1461032a578063a5cbe41514610332578063a778b04014610345578063d547741f14610358578063df9c49a31461036b578063f2fde38b1461037e57600080fd5b80637a1395aa1461028d5780638205bf6a146102a05780638c65c81f146102ba5780638da5cb5b146102e957806390c3f38f1461030457806391d148541461031757600080fd5b8063313ce56711610115578063313ce5671461021e57806336568abe1461023d57806350d25bcd14610250578063668a0f0214610267578063715018a6146102705780637284e4161461027857600080fd5b806301ffc9a7146101525780630720da521461017a57806307e2cea5146101c2578063248a9ca3146101e55780632f2ff15d14610209575b600080fd5b610165610160366004610831565b610391565b60405190151581526020015b60405180910390f35b6101a7610188366004610862565b6000908152600560205260409020805460018201546002909201549092565b60408051938452602084019290925290820152606001610171565b6101d7600080516020610b8983398151915281565b604051908152602001610171565b6101d76101f3366004610862565b6000908152600160208190526040909120015490565b61021c610217366004610897565b6103c8565b005b60035461022b9060ff1681565b60405160ff9091168152602001610171565b61021c61024b366004610897565b6103f4565b6002546000908152600560205260409020546101d7565b6101d760025481565b61021c61042c565b610280610440565b60405161017191906108c3565b61021c61029b366004610912565b6104ce565b6002546000908152600560205260409020600101546101d7565b6101a76102c8366004610862565b60056020526000908152604090208054600182015460029092015490919083565b6000546040516001600160a01b039091168152602001610171565b61021c61031236600461094b565b6104ec565b610165610325366004610897565b610504565b6101d7600081565b61021c6103403660046109fc565b61052f565b61021c610353366004610862565b610552565b61021c610366366004610897565b610605565b61021c6103793660046109fc565b61062b565b61021c61038c3660046109fc565b61064b565b60006001600160e01b03198216637965db0b60e01b14806103c257506301ffc9a760e01b6001600160e01b03198316145b92915050565b600082815260016020819052604090912001546103e48161068b565b6103ee8383610695565b50505050565b6001600160a01b038116331461041d5760405163334bd91960e11b815260040160405180910390fd5b610427828261070e565b505050565b61043461077b565b61043e60006107a8565b565b6004805461044d90610a17565b80601f016020809104026020016040519081016040528092919081815260200182805461047990610a17565b80156104c65780601f1061049b576101008083540402835291602001916104c6565b820191906000526020600020905b8154815290600101906020018083116104a957829003601f168201915b505050505081565b6104d661077b565b6003805460ff191660ff92909216919091179055565b6104f461077b565b60046105008282610aa1565b5050565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b61053761077b565b61054f600080516020610b8983398151915282610605565b50565b600080516020610b8983398151915261056a8161068b565b6002805490600061057a83610b61565b90915550506040805160608082018352848252426020808401828152438587018181526002805460009081526005865289902097518855925160018801555195820195909555548551888152918201929092529384019290925290917fa2987fc184b597e6ea540a9c61430be359db85ea42710881600a867fc8fba251910160405180910390a25050565b600082815260016020819052604090912001546106218161068b565b6103ee838361070e565b61063361077b565b61054f600080516020610b89833981519152826103c8565b61065361077b565b6001600160a01b03811661068257604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b61054f816107a8565b61054f81336107f8565b60006106a18383610504565b6107065760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45060016103c2565b5060006103c2565b600061071a8383610504565b156107065760008381526001602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016103c2565b6000546001600160a01b0316331461043e5760405163118cdaa760e01b8152336004820152602401610679565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6108028282610504565b6105005760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610679565b60006020828403121561084357600080fd5b81356001600160e01b03198116811461085b57600080fd5b9392505050565b60006020828403121561087457600080fd5b5035919050565b80356001600160a01b038116811461089257600080fd5b919050565b600080604083850312156108aa57600080fd5b823591506108ba6020840161087b565b90509250929050565b60006020808352835180602085015260005b818110156108f1578581018301518582016040015282016108d5565b506000604082860101526040601f19601f8301168501019250505092915050565b60006020828403121561092457600080fd5b813560ff8116811461085b57600080fd5b634e487b7160e01b600052604160045260246000fd5b60006020828403121561095d57600080fd5b813567ffffffffffffffff8082111561097557600080fd5b818401915084601f83011261098957600080fd5b81358181111561099b5761099b610935565b604051601f8201601f19908116603f011681019083821181831017156109c3576109c3610935565b816040528281528760208487010111156109dc57600080fd5b826020860160208301376000928101602001929092525095945050505050565b600060208284031215610a0e57600080fd5b61085b8261087b565b600181811c90821680610a2b57607f821691505b602082108103610a4b57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115610427576000816000526020600020601f850160051c81016020861015610a7a5750805b601f850160051c820191505b81811015610a9957828155600101610a86565b505050505050565b815167ffffffffffffffff811115610abb57610abb610935565b610acf81610ac98454610a17565b84610a51565b602080601f831160018114610b045760008415610aec5750858301515b600019600386901b1c1916600185901b178555610a99565b600085815260208120601f198616915b82811015610b3357888601518255948401946001909101908401610b14565b5085821015610b515787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060018201610b8157634e487b7160e01b600052601160045260246000fd5b506001019056fe68e79a7bf1e0bc45d0a330c573bc367f9cf464fd326078812f301165fbda4ef1a26469706673582212200ae61e1d2037e996110581219bb69b6d7faec35d0dd76043fb28ebc0b6f7665664736f6c6343000819003300000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000060000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae63700000000000000000000000000000000000000000000000000000000000000047465737400000000000000000000000000000000000000000000000000000000", - "nonce": "0x4b", - "chainId": "0x66eee" - }, - "additionalContracts": [], - "isFixedGasLimit": false - } - ], - "receipts": [ - { - "status": "0x1", - "cumulativeGasUsed": "0xe3f69", - "logs": [ - { - "address": "0x703dbc7c0ab7f69565fa459b55ff7e1123fd556a", - "topics": [ - "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae637" - ], - "data": "0x", - "blockHash": "0x8d8d392ecdc7b15a10b0b6c8904ca3703260a46e9400b36c341de99499107a28", - "blockNumber": "0x47e13c3", - "transactionHash": "0x67f99e24fb42c52825fc498a4ec6c0957e75816c7a8a7b1f3fbfeebb88ca754b", - "transactionIndex": "0x1", - "logIndex": "0x0", - "removed": false - }, - { - "address": "0x703dbc7c0ab7f69565fa459b55ff7e1123fd556a", - "topics": [ - "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae637", - "0x000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae637" - ], - "data": "0x", - "blockHash": "0x8d8d392ecdc7b15a10b0b6c8904ca3703260a46e9400b36c341de99499107a28", - "blockNumber": "0x47e13c3", - "transactionHash": "0x67f99e24fb42c52825fc498a4ec6c0957e75816c7a8a7b1f3fbfeebb88ca754b", - "transactionIndex": "0x1", - "logIndex": "0x1", - "removed": false - }, - { - "address": "0x703dbc7c0ab7f69565fa459b55ff7e1123fd556a", - "topics": [ - "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", - "0x68e79a7bf1e0bc45d0a330c573bc367f9cf464fd326078812f301165fbda4ef1", - "0x000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae637", - "0x000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae637" - ], - "data": "0x", - "blockHash": "0x8d8d392ecdc7b15a10b0b6c8904ca3703260a46e9400b36c341de99499107a28", - "blockNumber": "0x47e13c3", - "transactionHash": "0x67f99e24fb42c52825fc498a4ec6c0957e75816c7a8a7b1f3fbfeebb88ca754b", - "transactionIndex": "0x1", - "logIndex": "0x2", - "removed": false - } - ], - "logsBloom": "0x00000004000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000001000000000000000000000000000000200000000000000000000000000041000000000000000000000000000000000000020000000000000000010800000000000000000400000000000000400000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000100000000000020000800000000000000000400000000400000000000000000000000000000000000", - "type": "0x2", - "transactionHash": "0x67f99e24fb42c52825fc498a4ec6c0957e75816c7a8a7b1f3fbfeebb88ca754b", - "transactionIndex": "0x1", - "blockHash": "0x8d8d392ecdc7b15a10b0b6c8904ca3703260a46e9400b36c341de99499107a28", - "blockNumber": "0x47e13c3", - "gasUsed": "0xe3f69", - "effectiveGasPrice": "0x5f5e100", - "from": "0xc26d7ef337e01a5cc5498d3cc2ff0610761ae637", - "to": null, - "contractAddress": "0x703dbc7c0ab7f69565fa459b55ff7e1123fd556a", - "gasUsedForL1": "0x15b3f", - "l1BlockNumber": "0x64a9b8" - } - ], - "libraries": [], - "pending": [], - "returns": {}, - "timestamp": 1724978654, - "chain": 421614, - "commit": "81037c9" -} \ No newline at end of file diff --git a/broadcast/SimpleOracle.s.sol/421614/run-latest.json b/broadcast/SimpleOracle.s.sol/421614/run-latest.json deleted file mode 100644 index 98194e6..0000000 --- a/broadcast/SimpleOracle.s.sol/421614/run-latest.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "transactions": [ - { - "hash": "0x67f99e24fb42c52825fc498a4ec6c0957e75816c7a8a7b1f3fbfeebb88ca754b", - "transactionType": "CREATE", - "contractName": "SimpleOracle", - "contractAddress": "0x703dbc7c0ab7f69565fa459b55ff7e1123fd556a", - "function": null, - "arguments": [ - "18", - "test", - "0xc26d7EF337e01a5cC5498D3cc2ff0610761ae637" - ], - "transaction": { - "from": "0xc26d7ef337e01a5cc5498d3cc2ff0610761ae637", - "gas": "0x12ed14", - "value": "0x0", - "input": "0x608060405234801561001057600080fd5b5060405161100438038061100483398101604081905261002f916101da565b806001600160a01b03811661005e57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b610067816100c3565b506003805460ff191660ff851617905560046100838382610358565b5061008f600082610113565b506100ba7f68e79a7bf1e0bc45d0a330c573bc367f9cf464fd326078812f301165fbda4ef182610113565b50505050610417565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008281526001602090815260408083206001600160a01b038516845290915281205460ff1661019e5760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45060016101a2565b5060005b92915050565b634e487b7160e01b600052604160045260246000fd5b80516001600160a01b03811681146101d557600080fd5b919050565b6000806000606084860312156101ef57600080fd5b835160ff8116811461020057600080fd5b602085810151919450906001600160401b038082111561021f57600080fd5b818701915087601f83011261023357600080fd5b815181811115610245576102456101a8565b604051601f8201601f19908116603f0116810190838211818310171561026d5761026d6101a8565b816040528281528a8684870101111561028557600080fd5b600093505b828410156102a7578484018601518185018701529285019261028a565b60008684830101528097505050505050506102c4604085016101be565b90509250925092565b600181811c908216806102e157607f821691505b60208210810361030157634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115610353576000816000526020600020601f850160051c810160208610156103305750805b601f850160051c820191505b8181101561034f5782815560010161033c565b5050505b505050565b81516001600160401b03811115610371576103716101a8565b6103858161037f84546102cd565b84610307565b602080601f8311600181146103ba57600084156103a25750858301515b600019600386901b1c1916600185901b17855561034f565b600085815260208120601f198616915b828110156103e9578886015182559484019460019091019084016103ca565b50858210156104075787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b610bde806104266000396000f3fe608060405234801561001057600080fd5b506004361061014d5760003560e01c80637a1395aa116100c3578063a217fddf1161007c578063a217fddf1461032a578063a5cbe41514610332578063a778b04014610345578063d547741f14610358578063df9c49a31461036b578063f2fde38b1461037e57600080fd5b80637a1395aa1461028d5780638205bf6a146102a05780638c65c81f146102ba5780638da5cb5b146102e957806390c3f38f1461030457806391d148541461031757600080fd5b8063313ce56711610115578063313ce5671461021e57806336568abe1461023d57806350d25bcd14610250578063668a0f0214610267578063715018a6146102705780637284e4161461027857600080fd5b806301ffc9a7146101525780630720da521461017a57806307e2cea5146101c2578063248a9ca3146101e55780632f2ff15d14610209575b600080fd5b610165610160366004610831565b610391565b60405190151581526020015b60405180910390f35b6101a7610188366004610862565b6000908152600560205260409020805460018201546002909201549092565b60408051938452602084019290925290820152606001610171565b6101d7600080516020610b8983398151915281565b604051908152602001610171565b6101d76101f3366004610862565b6000908152600160208190526040909120015490565b61021c610217366004610897565b6103c8565b005b60035461022b9060ff1681565b60405160ff9091168152602001610171565b61021c61024b366004610897565b6103f4565b6002546000908152600560205260409020546101d7565b6101d760025481565b61021c61042c565b610280610440565b60405161017191906108c3565b61021c61029b366004610912565b6104ce565b6002546000908152600560205260409020600101546101d7565b6101a76102c8366004610862565b60056020526000908152604090208054600182015460029092015490919083565b6000546040516001600160a01b039091168152602001610171565b61021c61031236600461094b565b6104ec565b610165610325366004610897565b610504565b6101d7600081565b61021c6103403660046109fc565b61052f565b61021c610353366004610862565b610552565b61021c610366366004610897565b610605565b61021c6103793660046109fc565b61062b565b61021c61038c3660046109fc565b61064b565b60006001600160e01b03198216637965db0b60e01b14806103c257506301ffc9a760e01b6001600160e01b03198316145b92915050565b600082815260016020819052604090912001546103e48161068b565b6103ee8383610695565b50505050565b6001600160a01b038116331461041d5760405163334bd91960e11b815260040160405180910390fd5b610427828261070e565b505050565b61043461077b565b61043e60006107a8565b565b6004805461044d90610a17565b80601f016020809104026020016040519081016040528092919081815260200182805461047990610a17565b80156104c65780601f1061049b576101008083540402835291602001916104c6565b820191906000526020600020905b8154815290600101906020018083116104a957829003601f168201915b505050505081565b6104d661077b565b6003805460ff191660ff92909216919091179055565b6104f461077b565b60046105008282610aa1565b5050565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b61053761077b565b61054f600080516020610b8983398151915282610605565b50565b600080516020610b8983398151915261056a8161068b565b6002805490600061057a83610b61565b90915550506040805160608082018352848252426020808401828152438587018181526002805460009081526005865289902097518855925160018801555195820195909555548551888152918201929092529384019290925290917fa2987fc184b597e6ea540a9c61430be359db85ea42710881600a867fc8fba251910160405180910390a25050565b600082815260016020819052604090912001546106218161068b565b6103ee838361070e565b61063361077b565b61054f600080516020610b89833981519152826103c8565b61065361077b565b6001600160a01b03811661068257604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b61054f816107a8565b61054f81336107f8565b60006106a18383610504565b6107065760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45060016103c2565b5060006103c2565b600061071a8383610504565b156107065760008381526001602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016103c2565b6000546001600160a01b0316331461043e5760405163118cdaa760e01b8152336004820152602401610679565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6108028282610504565b6105005760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610679565b60006020828403121561084357600080fd5b81356001600160e01b03198116811461085b57600080fd5b9392505050565b60006020828403121561087457600080fd5b5035919050565b80356001600160a01b038116811461089257600080fd5b919050565b600080604083850312156108aa57600080fd5b823591506108ba6020840161087b565b90509250929050565b60006020808352835180602085015260005b818110156108f1578581018301518582016040015282016108d5565b506000604082860101526040601f19601f8301168501019250505092915050565b60006020828403121561092457600080fd5b813560ff8116811461085b57600080fd5b634e487b7160e01b600052604160045260246000fd5b60006020828403121561095d57600080fd5b813567ffffffffffffffff8082111561097557600080fd5b818401915084601f83011261098957600080fd5b81358181111561099b5761099b610935565b604051601f8201601f19908116603f011681019083821181831017156109c3576109c3610935565b816040528281528760208487010111156109dc57600080fd5b826020860160208301376000928101602001929092525095945050505050565b600060208284031215610a0e57600080fd5b61085b8261087b565b600181811c90821680610a2b57607f821691505b602082108103610a4b57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115610427576000816000526020600020601f850160051c81016020861015610a7a5750805b601f850160051c820191505b81811015610a9957828155600101610a86565b505050505050565b815167ffffffffffffffff811115610abb57610abb610935565b610acf81610ac98454610a17565b84610a51565b602080601f831160018114610b045760008415610aec5750858301515b600019600386901b1c1916600185901b178555610a99565b600085815260208120601f198616915b82811015610b3357888601518255948401946001909101908401610b14565b5085821015610b515787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060018201610b8157634e487b7160e01b600052601160045260246000fd5b506001019056fe68e79a7bf1e0bc45d0a330c573bc367f9cf464fd326078812f301165fbda4ef1a26469706673582212200ae61e1d2037e996110581219bb69b6d7faec35d0dd76043fb28ebc0b6f7665664736f6c6343000819003300000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000060000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae63700000000000000000000000000000000000000000000000000000000000000047465737400000000000000000000000000000000000000000000000000000000", - "nonce": "0x4b", - "chainId": "0x66eee" - }, - "additionalContracts": [], - "isFixedGasLimit": false - } - ], - "receipts": [ - { - "status": "0x1", - "cumulativeGasUsed": "0xe3f69", - "logs": [ - { - "address": "0x703dbc7c0ab7f69565fa459b55ff7e1123fd556a", - "topics": [ - "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae637" - ], - "data": "0x", - "blockHash": "0x8d8d392ecdc7b15a10b0b6c8904ca3703260a46e9400b36c341de99499107a28", - "blockNumber": "0x47e13c3", - "transactionHash": "0x67f99e24fb42c52825fc498a4ec6c0957e75816c7a8a7b1f3fbfeebb88ca754b", - "transactionIndex": "0x1", - "logIndex": "0x0", - "removed": false - }, - { - "address": "0x703dbc7c0ab7f69565fa459b55ff7e1123fd556a", - "topics": [ - "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae637", - "0x000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae637" - ], - "data": "0x", - "blockHash": "0x8d8d392ecdc7b15a10b0b6c8904ca3703260a46e9400b36c341de99499107a28", - "blockNumber": "0x47e13c3", - "transactionHash": "0x67f99e24fb42c52825fc498a4ec6c0957e75816c7a8a7b1f3fbfeebb88ca754b", - "transactionIndex": "0x1", - "logIndex": "0x1", - "removed": false - }, - { - "address": "0x703dbc7c0ab7f69565fa459b55ff7e1123fd556a", - "topics": [ - "0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d", - "0x68e79a7bf1e0bc45d0a330c573bc367f9cf464fd326078812f301165fbda4ef1", - "0x000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae637", - "0x000000000000000000000000c26d7ef337e01a5cc5498d3cc2ff0610761ae637" - ], - "data": "0x", - "blockHash": "0x8d8d392ecdc7b15a10b0b6c8904ca3703260a46e9400b36c341de99499107a28", - "blockNumber": "0x47e13c3", - "transactionHash": "0x67f99e24fb42c52825fc498a4ec6c0957e75816c7a8a7b1f3fbfeebb88ca754b", - "transactionIndex": "0x1", - "logIndex": "0x2", - "removed": false - } - ], - "logsBloom": "0x00000004000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000001000000000000000000000000000000200000000000000000000000000041000000000000000000000000000000000000020000000000000000010800000000000000000400000000000000400000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000100000000000020000800000000000000000400000000400000000000000000000000000000000000", - "type": "0x2", - "transactionHash": "0x67f99e24fb42c52825fc498a4ec6c0957e75816c7a8a7b1f3fbfeebb88ca754b", - "transactionIndex": "0x1", - "blockHash": "0x8d8d392ecdc7b15a10b0b6c8904ca3703260a46e9400b36c341de99499107a28", - "blockNumber": "0x47e13c3", - "gasUsed": "0xe3f69", - "effectiveGasPrice": "0x5f5e100", - "from": "0xc26d7ef337e01a5cc5498d3cc2ff0610761ae637", - "to": null, - "contractAddress": "0x703dbc7c0ab7f69565fa459b55ff7e1123fd556a", - "gasUsedForL1": "0x15b3f", - "l1BlockNumber": "0x64a9b8" - } - ], - "libraries": [], - "pending": [], - "returns": {}, - "timestamp": 1724978654, - "chain": 421614, - "commit": "81037c9" -} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 83816a2..cd47d12 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,5 +3,9 @@ src = "src" out = "out" libs = ["lib"] via_ir = true +ffi = true +ast = true +build_info = true +extra_output = ["storageLayout"] # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/report.md b/report.md index 6d344eb..e0922e7 100644 --- a/report.md +++ b/report.md @@ -15,9 +15,10 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati - [L-5: Define and use `constant` variables instead of using literals](#l-5-define-and-use-constant-variables-instead-of-using-literals) - [L-6: Event is missing `indexed` fields](#l-6-event-is-missing-indexed-fields) - [L-7: PUSH0 is not supported by all chains](#l-7-push0-is-not-supported-by-all-chains) - - [L-8: Loop contains `require`/`revert` statements](#l-8-loop-contains-requirerevert-statements) - - [L-9: Costly operations inside loops.](#l-9-costly-operations-inside-loops) - - [L-10: State variable changes but no event is emitted.](#l-10-state-variable-changes-but-no-event-is-emitted) + - [L-8: Empty Block](#l-8-empty-block) + - [L-9: Loop contains `require`/`revert` statements](#l-9-loop-contains-requirerevert-statements) + - [L-10: Costly operations inside loops.](#l-10-costly-operations-inside-loops) + - [L-11: State variable changes but no event is emitted.](#l-11-state-variable-changes-but-no-event-is-emitted) # Summary @@ -27,15 +28,15 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Key | Value | | --- | --- | | .sol Files | 1 | -| Total nSLOC | 155 | +| Total nSLOC | 168 | ## Files Details | Filepath | nSLOC | | --- | --- | -| src/EdgePushOracle.sol | 155 | -| **Total** | **155** | +| src/EdgePushOracle.sol | 168 | +| **Total** | **168** | ## Issue Summary @@ -43,7 +44,7 @@ This report was generated by [Aderyn](https://github.com/Cyfrin/aderyn), a stati | Category | No. of Issues | | --- | --- | | High | 0 | -| Low | 10 | +| Low | 11 | # Low Issues @@ -55,31 +56,31 @@ Contracts have owners with privileged rights to perform admin tasks and need to
5 Found Instances -- Found in src/EdgePushOracle.sol [Line: 11](src/EdgePushOracle.sol#L11) +- Found in src/EdgePushOracle.sol [Line: 71](src/EdgePushOracle.sol#L71) ```solidity - contract EdgePushOracle is Ownable { + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} ``` -- Found in src/EdgePushOracle.sol [Line: 59](src/EdgePushOracle.sol#L59) +- Found in src/EdgePushOracle.sol [Line: 79](src/EdgePushOracle.sol#L79) ```solidity - function addTrustedOracle(address oracle) external onlyOwner { + function addOracle(address oracle) external onlyOwner { ``` -- Found in src/EdgePushOracle.sol [Line: 70](src/EdgePushOracle.sol#L70) +- Found in src/EdgePushOracle.sol [Line: 90](src/EdgePushOracle.sol#L90) ```solidity - function removeTrustedOracle(address oracle) external onlyOwner { + function removeOracle(address oracle) external onlyOwner { ``` -- Found in src/EdgePushOracle.sol [Line: 224](src/EdgePushOracle.sol#L224) +- Found in src/EdgePushOracle.sol [Line: 244](src/EdgePushOracle.sol#L244) ```solidity function setDescription(string memory _description) external onlyOwner { ``` -- Found in src/EdgePushOracle.sol [Line: 232](src/EdgePushOracle.sol#L232) +- Found in src/EdgePushOracle.sol [Line: 252](src/EdgePushOracle.sol#L252) ```solidity function setDecimals(uint8 _decimals) external onlyOwner { @@ -96,10 +97,10 @@ The `ecrecover` function is susceptible to signature malleability. This means th
1 Found Instances -- Found in src/EdgePushOracle.sol [Line: 285](src/EdgePushOracle.sol#L285) +- Found in src/EdgePushOracle.sol [Line: 311](src/EdgePushOracle.sol#L311) ```solidity - return ecrecover(_messageHash, v, r, s); // Recover and return signer address + address recovered = ecrecover(_messageHash, v, r, s); ```
@@ -130,10 +131,10 @@ Instead of marking a function as `public`, consider marking it as `external` if
1 Found Instances -- Found in src/EdgePushOracle.sol [Line: 170](src/EdgePushOracle.sol#L170) +- Found in src/EdgePushOracle.sol [Line: 56](src/EdgePushOracle.sol#L56) ```solidity - function getRoundData(uint80 round) + function initialize(uint8 _decimals, string memory _description, address _owner) public initializer { ```
@@ -147,22 +148,22 @@ If the same constant literal value is used multiple times, create a constant sta
3 Found Instances -- Found in src/EdgePushOracle.sol [Line: 277](src/EdgePushOracle.sol#L277) +- Found in src/EdgePushOracle.sol [Line: 299](src/EdgePushOracle.sol#L299) ```solidity if (v < 27) { ``` -- Found in src/EdgePushOracle.sol [Line: 278](src/EdgePushOracle.sol#L278) +- Found in src/EdgePushOracle.sol [Line: 300](src/EdgePushOracle.sol#L300) ```solidity - v += 27; // Adjust v + v += 27; // Adjust v to be 27 or 28 ``` -- Found in src/EdgePushOracle.sol [Line: 282](src/EdgePushOracle.sol#L282) +- Found in src/EdgePushOracle.sol [Line: 302](src/EdgePushOracle.sol#L302) ```solidity - require(v == 27 || v == 28, "Invalid signature v value"); // Check v value + require(v == 27 || v == 28, "Invalid signature 'v' value"); // Check v value ```
@@ -176,7 +177,7 @@ Index event fields make the field more quickly accessible to off-chain tools tha
1 Found Instances -- Found in src/EdgePushOracle.sol [Line: 37](src/EdgePushOracle.sol#L37) +- Found in src/EdgePushOracle.sol [Line: 39](src/EdgePushOracle.sol#L39) ```solidity event NewPriceUpdate( @@ -203,14 +204,31 @@ Solc compiler version 0.8.20 switches the default target EVM version to Shanghai -## L-8: Loop contains `require`/`revert` statements +## L-8: Empty Block + +Consider removing empty blocks. + +
1 Found Instances + + +- Found in src/EdgePushOracle.sol [Line: 71](src/EdgePushOracle.sol#L71) + + ```solidity + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + ``` + +
+ + + +## L-9: Loop contains `require`/`revert` statements Avoid `require` / `revert` statements in a loop because a single bad item can cause the whole transaction to fail. It's better to forgive on fail and return failed elements post processing of the loop
1 Found Instances -- Found in src/EdgePushOracle.sol [Line: 108](src/EdgePushOracle.sol#L108) +- Found in src/EdgePushOracle.sol [Line: 128](src/EdgePushOracle.sol#L128) ```solidity for (uint256 i = 0; i < numSignatures; i++) { @@ -220,14 +238,14 @@ Avoid `require` / `revert` statements in a loop because a single bad item can ca -## L-9: Costly operations inside loops. +## L-10: Costly operations inside loops. Invoking `SSTORE`operations in loops may lead to Out-of-gas errors. Use a local variable to hold the loop computation result.
1 Found Instances -- Found in src/EdgePushOracle.sol [Line: 74](src/EdgePushOracle.sol#L74) +- Found in src/EdgePushOracle.sol [Line: 94](src/EdgePushOracle.sol#L94) ```solidity for (uint256 i = 0; i < oracles.length; i++) { @@ -237,20 +255,26 @@ Invoking `SSTORE`operations in loops may lead to Out-of-gas errors. Use a local -## L-10: State variable changes but no event is emitted. +## L-11: State variable changes but no event is emitted. State variable changes in this function but no event is emitted. -
2 Found Instances +
3 Found Instances -- Found in src/EdgePushOracle.sol [Line: 224](src/EdgePushOracle.sol#L224) +- Found in src/EdgePushOracle.sol [Line: 56](src/EdgePushOracle.sol#L56) + + ```solidity + function initialize(uint8 _decimals, string memory _description, address _owner) public initializer { + ``` + +- Found in src/EdgePushOracle.sol [Line: 244](src/EdgePushOracle.sol#L244) ```solidity function setDescription(string memory _description) external onlyOwner { ``` -- Found in src/EdgePushOracle.sol [Line: 232](src/EdgePushOracle.sol#L232) +- Found in src/EdgePushOracle.sol [Line: 252](src/EdgePushOracle.sol#L252) ```solidity function setDecimals(uint8 _decimals) external onlyOwner { diff --git a/script/EdgePushOracle.s.sol b/script/EdgePushOracle.s.sol index afd3421..35fa716 100644 --- a/script/EdgePushOracle.s.sol +++ b/script/EdgePushOracle.s.sol @@ -3,27 +3,41 @@ pragma solidity ^0.8.0; import "forge-std/Script.sol"; import "../src/EdgePushOracle.sol"; +import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; +// Contract for deploying EdgePushOracle with UUPS proxy pattern contract DeployEdgePushOracle is Script { function run() external { - // Load the deployer's private key + // Load the deployer's private key from environment variable uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - // Start broadcasting transactions + address[] memory trustedOracles = new address[](5); + trustedOracles[0] = 0xb42F25C68901b9291a488d68536f449538a7a182; + trustedOracles[1] = 0xBb5e07e62Af4131e504a430EA608FE719E8db18A; + trustedOracles[2] = 0xf1a2e6b10c24B0Db668e4D6654aD746Af776f27e; + trustedOracles[3] = 0xe301279400AB18d9870065b4dc3fF0bf984EeE06; + trustedOracles[4] = 0x152e901D6E71e95dAA31B13767a8589E20b99Aa3; + + // Start broadcasting transactions using the deployer's private key vm.startBroadcast(deployerPrivateKey); - // Set the owner address as the desired deployer address or another address + // Set the owner address as the address derived from the deployer's private key address ownerAddress = vm.addr(deployerPrivateKey); - // Deploy the SimpleOracle contract with the owner address - EdgePushOracle edgePushOracle = new EdgePushOracle( - 18, // decimals - "test", // description - ownerAddress // owner address + // Deploy the EdgePushOracle implementation contract + EdgePushOracle edgePushOracleImplementation = new EdgePushOracle(); + + // Log the deployed implementation contract address for verification + console.log("Deployed EdgePushOracle implementation at", address(edgePushOracleImplementation)); + + // Deploy the UUPS proxy pointing to the implementation + // Note: Make sure the initialize parameters (8, "test", owner) are correct for your use case + address proxy = Upgrades.deployUUPSProxy( + "EdgePushOracle.sol", abi.encodeCall(EdgePushOracle.initialize, (8, "test", ownerAddress, trustedOracles)) ); - // Log the deployed contract address - console.log("Deployed EdgePushOracle contract at", address(edgePushOracle)); + // Log the deployed proxy contract address for verification + console.log("Deployed EdgePushOracle proxy at", proxy); // Stop broadcasting transactions vm.stopBroadcast(); diff --git a/src/EdgePushOracle.sol b/src/EdgePushOracle.sol index 1df00b2..7638aea 100644 --- a/src/EdgePushOracle.sol +++ b/src/EdgePushOracle.sol @@ -1,14 +1,16 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.25; -import "openzeppelin-contracts/contracts/access/Ownable.sol"; +import "openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol"; +import "openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; +import "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol"; /** * @title EdgePushOracle * @dev A decentralized oracle contract that allows trusted oracles to push price updates - * with multi-signature verification. + * with multi-signature verification. Upgradable using UUPS proxy pattern. */ -contract EdgePushOracle is Ownable { +contract EdgePushOracle is Initializable, OwnableUpgradeable, UUPSUpgradeable { // ============ Structs ============ struct RoundData { @@ -22,9 +24,9 @@ contract EdgePushOracle is Ownable { // ============ State Variables ============ - uint80 public latestRound = 0; // Tracks the latest round number, initialized to 0 uint8 public decimals; // Number of decimal places for price string public description; // Description of the oracle + uint80 internal _latestRound; // Tracks the latest round number, initialized to 0 mapping(uint80 => RoundData) public rounds; // Mapping of round number to RoundData mapping(address => bool) public trustedOracles; // Mapping of trusted oracle addresses @@ -43,13 +45,44 @@ contract EdgePushOracle is Ownable { uint256 numSignatures ); // Event emitted for new price update - // ============ Constructor ============ + // ============ Initializer ============ + + /** + * @notice Initializes the contract instead of using a constructor + * @param _decimals Number of decimal places for price + * @param _description Description of the oracle + * @param _owner Address of the contract owner + * @param _oracles Array of oracle addresses to be added + */ + function initialize(uint8 _decimals, string memory _description, address _owner, address[] memory _oracles) + public + initializer + { + __Ownable_init(_owner); + __UUPSUpgradeable_init(); - constructor(uint8 _decimals, string memory _description, address _owner) Ownable(_owner) { decimals = _decimals; // Set the number of decimals description = _description; // Set the description + _latestRound = 0; + + // Add initial oracles + for (uint256 i = 0; i < _oracles.length; i++) { + address oracle = _oracles[i]; + require(!trustedOracles[oracle], "Oracle already trusted"); // Check if oracle is already trusted + trustedOracles[oracle] = true; // Mark oracle as trusted + oracles.push(oracle); // Add oracle to the list + } } + // ============ Upgrade Authorization ============ + + /** + * @dev Function that authorizes an upgrade to a new implementation. + * Only the owner can upgrade the contract. + * @param newImplementation Address of the new implementation contract + */ + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + // ============ Oracle Management Functions ============ /** @@ -93,7 +126,7 @@ contract EdgePushOracle is Ownable { (int256 price, uint256 reportRoundId, uint256 observationTs) = abi.decode(report, (int256, uint256, uint256)); // Timestamp checks - require(observationTs > rounds[latestRound].observedTs, "Report timestamp is not newer"); // Ensure new timestamp + require(observationTs > rounds[_latestRound].observedTs, "Report timestamp is not newer"); // Ensure new timestamp require(observationTs <= block.timestamp + 5 minutes, "Report timestamp too far in the future"); // Check future timestamp uint256 minAllowedTimestamp = block.timestamp > 1 hours ? block.timestamp - 1 hours : 0; // Calculate minimum allowed timestamp @@ -125,9 +158,9 @@ contract EdgePushOracle is Ownable { require(validSignatures >= requiredSignatures(), "Not enough signatures"); // Ensure enough valid signatures - require(latestRound < type(uint80).max, "Latest round exceeds uint80 limit"); // Check round limit - latestRound++; // Increment latest round - rounds[latestRound] = RoundData({ + require(_latestRound < type(uint80).max, "Latest round exceeds uint80 limit"); // Check round limit + _latestRound++; // Increment latest round + rounds[_latestRound] = RoundData({ price: price, reportRoundId: reportRoundId, observedTs: observationTs, @@ -136,7 +169,7 @@ contract EdgePushOracle is Ownable { numSignatures: uint8(validSignatures) // Store valid signatures as uint8 }); // Store round data - emit NewPriceUpdate(latestRound, price, reportRoundId, observationTs, msg.sender, validSignatures); // Emit new price update event + emit NewPriceUpdate(_latestRound, price, reportRoundId, observationTs, msg.sender, validSignatures); // Emit new price update event } // ============ Utility Functions ============ @@ -148,17 +181,25 @@ contract EdgePushOracle is Ownable { function requiredSignatures() public view returns (uint256) { uint256 totalOracles = oracles.length; // Get total number of oracles uint256 threshold = (totalOracles * 2) / 3; // Calculate threshold for majority - if (threshold > totalOracles) { - threshold = totalOracles; // Adjust threshold if necessary - } - if (threshold == 0) { - threshold = 1; // At least one signature required - } - return threshold; // Return required signatures + return threshold > 0 ? threshold : 1; // Return required signatures } // ============ Data Retrieval Functions ============ + function latestRound() external view returns (uint256) { + return uint256(_latestRound); // Return the latest round number + } + + function getAnswer(uint256 roundId) external view returns (int256) { + require(roundId > 0 && roundId <= _latestRound, "Round is not yet available"); // Check round availability + return rounds[uint80(roundId)].price; // Return the price for the specified round + } + + function getTimestamp(uint256 roundId) external view returns (uint256) { + require(roundId > 0 && roundId <= _latestRound, "Round is not yet available"); // Check round availability + return rounds[uint80(roundId)].postedTs; // Return the timestamp for the specified round + } + /** * @notice Retrieve round data for a specific round * @param round The round number to retrieve data for @@ -172,19 +213,11 @@ contract EdgePushOracle is Ownable { view returns (int256 price, uint256 reportRoundId, uint256 timestamp, uint256 blockNumber) { - require(round > 0 && round <= latestRound, "Round is not yet available"); // Check round availability + require(round > 0 && round <= _latestRound, "Round is not yet available"); // Check round availability RoundData storage data = rounds[round]; // Get round data return (data.price, data.reportRoundId, data.observedTs, data.blockNumber); // Return round data } - /** - * @notice Retrieve the latest price - * @return price The latest reported price - */ - function latestPrice() external view returns (int256 price) { - return rounds[latestRound].price; // Return latest price - } - /** * @notice Returns details of the latest successful update round * @return roundId The number of the latest round @@ -199,9 +232,9 @@ contract EdgePushOracle is Ownable { virtual returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { - roundId = uint80(latestRound); // Get latest round ID + roundId = uint80(_latestRound); // Get latest round ID answer = latestAnswer(); // Get latest answer - RoundData storage data = rounds[latestRound]; // Get latest round data + RoundData storage data = rounds[_latestRound]; // Get latest round data startedAt = data.observedTs; // Get start timestamp updatedAt = data.postedTs; // Get update timestamp answeredInRound = roundId; // Set answered in round @@ -212,7 +245,7 @@ contract EdgePushOracle is Ownable { * @return timestamp The timestamp of the latest round */ function latestTimestamp() external view returns (uint256 timestamp) { - return rounds[latestRound].postedTs; // Return latest round timestamp + return rounds[_latestRound].postedTs; // Return latest round timestamp } // ============ Admin Functions ============ @@ -249,7 +282,7 @@ contract EdgePushOracle is Ownable { * @return latestAnswer The latest successfully reported value */ function latestAnswer() public view virtual returns (int256) { - return rounds[latestRound].price; // Return latest answer + return rounds[_latestRound].price; // Return latest answer } /** @@ -262,26 +295,35 @@ contract EdgePushOracle is Ownable { require(_signature.length == 65, "Invalid signature length"); // Check signature length // Extract the signature components: v, r, and s - bytes32 r; // r component - bytes32 s; // s component - uint8 v; // v component + bytes32 r; + bytes32 s; + uint8 v; - // Signatures are in the format {r}{s}{v}, we extract them from the passed signature assembly { - r := mload(add(_signature, 0x20)) // Load r - s := mload(add(_signature, 0x40)) // Load s - v := byte(0, mload(add(_signature, 0x60))) // Load v + // First 32 bytes after length prefix + r := mload(add(_signature, 0x20)) + // Second 32 bytes + s := mload(add(_signature, 0x40)) + // Final byte (first byte of the next 32 bytes) + v := byte(0, mload(add(_signature, 0x60))) } - // Normalize v for chain IDs greater than 36 + // Adjust v value if necessary if (v < 27) { - v += 27; // Adjust v + v += 27; // Adjust v to be 27 or 28 } + require(v == 27 || v == 28, "Invalid signature 'v' value"); // Check v value + + // Enforce lower half order for s to prevent malleable signatures + uint256 sInt = uint256(s); + require( + sInt <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "Invalid signature 's' value" + ); - // Ensure it's a valid value for v (27 or 28 are the only valid recovery IDs in Ethereum) - require(v == 27 || v == 28, "Invalid signature v value"); // Check v value + // Recover the address + address recovered = ecrecover(_messageHash, v, r, s); + require(recovered != address(0), "Invalid signature"); - // ecrecover returns the public key in Ethereum style (the address) - return ecrecover(_messageHash, v, r, s); // Recover and return signer address + return recovered; } } diff --git a/test/EdgePushOracle.t.sol b/test/EdgePushOracle.t.sol index c1752d0..d726a51 100644 --- a/test/EdgePushOracle.t.sol +++ b/test/EdgePushOracle.t.sol @@ -2,12 +2,10 @@ pragma solidity ^0.8.25; import "forge-std/Test.sol"; +import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import "../src/EdgePushOracle.sol"; -import "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol"; contract EdgePushOracleTest is Test { - using ECDSA for bytes32; - EdgePushOracle public edgePushOracle; address public owner; address public oracle1; @@ -29,7 +27,14 @@ contract EdgePushOracleTest is Test { oracle2 = vm.addr(privateKey2); oracle3 = vm.addr(privateKey3); - edgePushOracle = new EdgePushOracle(8, "Test Oracle", owner); + address[] memory trustedOracles = new address[](0); + + address proxy = Upgrades.deployUUPSProxy( + "EdgePushOracle.sol", abi.encodeCall(EdgePushOracle.initialize, (8, "test", owner, trustedOracles)) + ); + + // Assign edgePushOracle to point to the deployed proxy + edgePushOracle = EdgePushOracle(proxy); // Set block.timestamp to a non-zero value vm.warp(1 hours); // Set block.timestamp to 1 hour (3600 seconds) @@ -74,8 +79,8 @@ contract EdgePushOracleTest is Test { edgePushOracle.postUpdate(report, signatures); - (uint80 roundId, int256 latestPrice,,,) = edgePushOracle.latestRoundData(); - assertEq(latestPrice, price, "The latest price should match the posted price"); + (uint80 roundId, int256 latestAnswer,,,) = edgePushOracle.latestRoundData(); + assertEq(latestAnswer, price, "The latest price should match the posted price"); assertEq(roundId, 1, "Round ID should be 1"); } @@ -156,32 +161,6 @@ contract EdgePushOracleTest is Test { edgePushOracle.postUpdate(report, signatures); } - function testLatestPriceRetrieval() public { - edgePushOracle.addOracle(oracle1); - edgePushOracle.addOracle(oracle2); - - int256 price = 200; - uint256 reportRoundId = 1; - uint256 observationTs = block.timestamp; // observationTs is 3600 - - bytes memory report = abi.encode(price, reportRoundId, observationTs); - bytes32 reportHash = keccak256(report); - - (uint8 v1, bytes32 r1, bytes32 s1) = vm.sign(privateKey1, reportHash); - (uint8 v2, bytes32 r2, bytes32 s2) = vm.sign(privateKey2, reportHash); - - bytes memory signature1 = abi.encodePacked(r1, s1, v1); - bytes memory signature2 = abi.encodePacked(r2, s2, v2); - - bytes[] memory signatures = new bytes[](2); - signatures[0] = signature1; - signatures[1] = signature2; - - edgePushOracle.postUpdate(report, signatures); - - assertEq(edgePushOracle.latestPrice(), price, "The latest price should be the posted price"); - } - function testRoundDataRetrieval() public { edgePushOracle.addOracle(oracle1); edgePushOracle.addOracle(oracle2); @@ -262,8 +241,8 @@ contract EdgePushOracleTest is Test { edgePushOracle.postUpdate(report, signatures); - (uint80 roundId, int256 latestPrice,,,) = edgePushOracle.latestRoundData(); - assertEq(latestPrice, 99988501, "The latest price should match the posted price"); + (uint80 roundId, int256 latestAnswer,,,) = edgePushOracle.latestRoundData(); + assertEq(latestAnswer, 99988501, "The latest price should match the posted price"); assertEq(roundId, 1, "Round ID should match the posted round ID"); } @@ -306,7 +285,6 @@ contract EdgePushOracleTest is Test { uint256 observationTs = block.timestamp; bytes memory report = abi.encode(price, reportRoundId, observationTs); - bytes32 reportHash = keccak256(report); bytes[] memory signatures = new bytes[](0); // No signatures @@ -323,7 +301,6 @@ contract EdgePushOracleTest is Test { uint256 observationTs = block.timestamp; bytes memory report = abi.encode(price, reportRoundId, observationTs); - bytes32 reportHash = keccak256(report); bytes[] memory signatures = new bytes[](0); // No signatures @@ -363,4 +340,109 @@ contract EdgePushOracleTest is Test { requiredSigs = edgePushOracle.requiredSignatures(); assertEq(requiredSigs, 3, "Required signatures should be 3 with 5 oracles"); } + + function testLatestRound() public { + edgePushOracle.addOracle(oracle1); + edgePushOracle.addOracle(oracle2); + + int256 price = 100; + uint256 reportRoundId = 1; + uint256 observationTs = block.timestamp; + + bytes memory report = abi.encode(price, reportRoundId, observationTs); + bytes32 reportHash = keccak256(report); + + (uint8 v1, bytes32 r1, bytes32 s1) = vm.sign(privateKey1, reportHash); + (uint8 v2, bytes32 r2, bytes32 s2) = vm.sign(privateKey2, reportHash); + + bytes memory signature1 = abi.encodePacked(r1, s1, v1); + bytes memory signature2 = abi.encodePacked(r2, s2, v2); + + bytes[] memory signatures = new bytes[](2); + signatures[0] = signature1; + signatures[1] = signature2; + + edgePushOracle.postUpdate(report, signatures); + + assertEq(edgePushOracle.latestRound(), 1, "Latest round should be 1"); + } + + function testGetAnswer() public { + edgePushOracle.addOracle(oracle1); + edgePushOracle.addOracle(oracle2); + + int256 price = 100; + uint256 reportRoundId = 1; + uint256 observationTs = block.timestamp; + + bytes memory report = abi.encode(price, reportRoundId, observationTs); + bytes32 reportHash = keccak256(report); + + (uint8 v1, bytes32 r1, bytes32 s1) = vm.sign(privateKey1, reportHash); + (uint8 v2, bytes32 r2, bytes32 s2) = vm.sign(privateKey2, reportHash); + + bytes memory signature1 = abi.encodePacked(r1, s1, v1); + bytes memory signature2 = abi.encodePacked(r2, s2, v2); + + bytes[] memory signatures = new bytes[](2); + signatures[0] = signature1; + signatures[1] = signature2; + + edgePushOracle.postUpdate(report, signatures); + + assertEq(edgePushOracle.getAnswer(1), price, "The answer for round 1 should be the posted price"); + } + + function testGetTimestamp() public { + edgePushOracle.addOracle(oracle1); + edgePushOracle.addOracle(oracle2); + + int256 price = 100; + uint256 reportRoundId = 1; + uint256 observationTs = block.timestamp; + + bytes memory report = abi.encode(price, reportRoundId, observationTs); + bytes32 reportHash = keccak256(report); + + (uint8 v1, bytes32 r1, bytes32 s1) = vm.sign(privateKey1, reportHash); + (uint8 v2, bytes32 r2, bytes32 s2) = vm.sign(privateKey2, reportHash); + + bytes memory signature1 = abi.encodePacked(r1, s1, v1); + bytes memory signature2 = abi.encodePacked(r2, s2, v2); + + bytes[] memory signatures = new bytes[](2); + signatures[0] = signature1; + signatures[1] = signature2; + + edgePushOracle.postUpdate(report, signatures); + + assertEq( + edgePushOracle.getTimestamp(1), block.timestamp, "The timestamp for round 1 should be the block timestamp" + ); + } + + function testTransferOwnership() public { + // Verify the initial owner is the test contract (address(this)) + assertEq(edgePushOracle.owner(), owner, "Initial owner should be the deployer (test contract)"); + + // Create a new address to transfer ownership to + address newOwner = vm.addr(0xABCD); + + // Transfer ownership to newOwner + edgePushOracle.transferOwnership(newOwner); + + // Verify that the owner has been updated + assertEq(edgePushOracle.owner(), newOwner, "Owner should be updated to newOwner"); + + // Try to call an onlyOwner function from the old owner (should fail) + vm.expectRevert(); + edgePushOracle.setDescription("Should fail"); + + // Switch to newOwner and update the description + vm.prank(newOwner); + edgePushOracle.setDescription("Updated by new owner"); + + // Verify that the description was updated + assertEq(edgePushOracle.description(), "Updated by new owner", "Description should be updated by new owner"); + } }