Update price evm Oracle Example (#1411)

This commit is contained in:
Aditya Arora 2024-04-02 14:42:46 -04:00 committed by GitHub
parent c2fde0f6dc
commit c0c03945d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 83 additions and 35 deletions

View File

@ -17,14 +17,20 @@ All of the commands in this section expect to be run from the `contract` directo
### Building
You need to have [Foundry](https://getfoundry.sh/) and `node` installed to run this example.
Once you have installed these tools, run the following commands from the [`contract`](./contract) directory:
Once you have installed these tools, run the following commands from the [`contract`](./contract) directory to install openzeppelin and forge dependencies:
```
forge install foundry-rs/forge-std@2c7cbfc6fbede6d7c9e6b17afe997e3fdfe22fef --no-git --no-commit
forge install pyth-network/pyth-sdk-solidity@v2.2.0 --no-git --no-commit
forge install foundry-rs/forge-std@v1.8.0 --no-git --no-commit
forge install OpenZeppelin/openzeppelin-contracts@v4.8.1 --no-git --no-commit
```
After installing the above dependencies, you need to install pyth-sdk-solidity.
```
npm init -y
npm install @pythnetwork/pyth-sdk-solidity
```
### Testing
Simply run `forge test` in the [`contract`](./contract) directory. This command will run the

View File

@ -0,0 +1,28 @@
{
"name": "contract",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "contract",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@pythnetwork/pyth-sdk-solidity": "^3.0.0"
}
},
"node_modules/@pythnetwork/pyth-sdk-solidity": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-solidity/-/pyth-sdk-solidity-3.0.0.tgz",
"integrity": "sha512-vM1zTVDOnjT2Tqp1IQQDFlwszI62+vBcUE+lRyUjHDnHrCO7wPu2/Sefdza/VcdbpZZuqxa5IO98AO0eUv44WA=="
}
},
"dependencies": {
"@pythnetwork/pyth-sdk-solidity": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-solidity/-/pyth-sdk-solidity-3.0.0.tgz",
"integrity": "sha512-vM1zTVDOnjT2Tqp1IQQDFlwszI62+vBcUE+lRyUjHDnHrCO7wPu2/Sefdza/VcdbpZZuqxa5IO98AO0eUv44WA=="
}
}
}

View File

@ -0,0 +1,19 @@
{
"name": "contract",
"version": "1.0.0",
"description": "",
"main": "index.js",
"directories": {
"lib": "lib",
"test": "test"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@pythnetwork/pyth-sdk-solidity": "^3.0.0"
}
}

View File

@ -1,4 +1,5 @@
ds-test/=lib/forge-std/lib/ds-test/src/
ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/
erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/
forge-std/=lib/forge-std/src/
pyth-sdk-solidity/=lib/pyth-sdk-solidity/
openzeppelin-contracts/=lib/openzeppelin-contracts/
@pythnetwork=node_modules/@pythnetwork

View File

@ -1,8 +1,9 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "pyth-sdk-solidity/IPyth.sol";
import "pyth-sdk-solidity/PythStructs.sol";
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
import "@pythnetwork/pyth-sdk-solidity/PythUtils.sol";
import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
// Example oracle AMM powered by Pyth price feeds.
@ -17,7 +18,12 @@ import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
// some quantity of both the base and quote token in order to function properly (using the ERC20 transfer function to
// the contract's address).
contract OracleSwap {
event Transfer(address from, address to, uint amountUsd, uint amountWei);
event Transfer(
address from,
address to,
uint256 amountUsd,
uint256 amountWei
);
IPyth pyth;
@ -53,10 +59,10 @@ contract OracleSwap {
// See the frontend code for an example of how to retrieve this data and pass it to this function.
function swap(
bool isBuy,
uint size,
uint256 size,
bytes[] calldata pythUpdateData
) external payable {
uint updateFee = pyth.getUpdateFee(pythUpdateData);
uint256 updateFee = pyth.getUpdateFee(pythUpdateData);
pyth.updatePriceFeeds{value: updateFee}(pythUpdateData);
PythStructs.Price memory currentBasePrice = pyth.getPrice(
@ -69,8 +75,16 @@ contract OracleSwap {
// Note: this code does all arithmetic with 18 decimal points. This approach should be fine for most
// price feeds, which typically have ~8 decimals. You can check the exponent on the price feed to ensure
// this doesn't lose precision.
uint256 basePrice = convertToUint(currentBasePrice, 18);
uint256 quotePrice = convertToUint(currentQuotePrice, 18);
uint256 basePrice = PythUtils.convertToUint(
currentBasePrice.price,
currentBasePrice.expo,
18
);
uint256 quotePrice = PythUtils.convertToUint(
currentQuotePrice.price,
currentQuotePrice.expo,
18
);
// This computation loses precision. The infinite-precision result is between [quoteSize, quoteSize + 1]
// We need to round this result in favor of the contract.
@ -90,28 +104,6 @@ contract OracleSwap {
}
}
// TODO: we should probably move something like this into the solidity sdk
function convertToUint(
PythStructs.Price memory price,
uint8 targetDecimals
) private pure returns (uint256) {
if (price.price < 0 || price.expo > 0 || price.expo < -255) {
revert();
}
uint8 priceDecimals = uint8(uint32(-1 * price.expo));
if (targetDecimals >= priceDecimals) {
return
uint(uint64(price.price)) *
10 ** uint32(targetDecimals - priceDecimals);
} else {
return
uint(uint64(price.price)) /
10 ** uint32(priceDecimals - targetDecimals);
}
}
// Get the number of base tokens in the pool
function baseBalance() public view returns (uint256) {
return baseToken.balanceOf(address(this));

View File

@ -3,7 +3,7 @@ pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "../src/OracleSwap.sol";
import "pyth-sdk-solidity/MockPyth.sol";
import "@pythnetwork/pyth-sdk-solidity/MockPyth.sol";
import "openzeppelin-contracts/contracts/mocks/ERC20Mock.sol";
contract OracleSwapTest is Test {
@ -84,6 +84,7 @@ contract OracleSwapTest is Test {
-5,
basePrice * 100000,
10 * 100000,
uint64(block.timestamp),
uint64(block.timestamp)
);
updateData[1] = mockPyth.createPriceFeedUpdateData(
@ -93,6 +94,7 @@ contract OracleSwapTest is Test {
-5,
quotePrice * 100000,
10 * 100000,
uint64(block.timestamp),
uint64(block.timestamp)
);