-
Notifications
You must be signed in to change notification settings - Fork 212
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
Create a Math Library for Combining Pyth Prices on Solidity #1746
Open
0xHaku
wants to merge
10
commits into
pyth-network:main
Choose a base branch
from
0xHaku:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
95be41d
add Price Library
0xHaku 223d685
fix visibility
0xHaku 121b3cd
Merge branch 'pyth-network:main' into main
0xHaku 3f10fc6
fix functions name style
0xHaku 8ec2ba0
Merge branch 'pyth-network:main' into main
0xHaku 7986f74
apply review comments
0xHaku 76b0a12
rename price to pythprice
0xHaku 82dccf1
Merge branch 'pyth-network:main' into main
0xHaku bc71898
fix snake case to camel case
0xHaku b0844f7
add price test with harness
0xHaku File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
281 changes: 281 additions & 0 deletions
281
target_chains/ethereum/contracts/forge-test/Pyth.Price.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
pragma solidity ^0.8.0; | ||
|
||
import "forge-std/Test.sol"; | ||
|
||
import "@pythnetwork/pyth-sdk-solidity/PythPrice.sol"; | ||
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol"; | ||
|
||
contract PythPriceTest is Test { | ||
PythPriceHarness harness; | ||
uint64 private constant PD_SCALE = 1_000_000_000; | ||
|
||
function setUp() public { | ||
harness = new PythPriceHarness(); | ||
} | ||
|
||
function testDivNormalOperation() public { | ||
PythStructs.Price memory price1 = PythStructs.Price({ | ||
price: 100, | ||
conf: 5, | ||
expo: -8, | ||
publishTime: 1000 | ||
}); | ||
PythStructs.Price memory price2 = PythStructs.Price({ | ||
price: 50, | ||
conf: 2, | ||
expo: -8, | ||
publishTime: 1100 | ||
}); | ||
PythStructs.Price memory result = harness.div(price1, price2); | ||
assertEq(result.price, 2 * 10 ** 9, "Division result should be 2"); | ||
assertEq(result.expo, -9, "Exponent should be -9"); | ||
assertEq( | ||
result.publishTime, | ||
1000, | ||
"PublishTime should be the minimum of the two" | ||
); | ||
} | ||
|
||
function testDivByZero() public { | ||
PythStructs.Price memory price1 = PythStructs.Price({ | ||
price: 100, | ||
conf: 5, | ||
expo: -8, | ||
publishTime: 1000 | ||
}); | ||
PythStructs.Price memory price2 = PythStructs.Price({ | ||
price: 0, | ||
conf: 2, | ||
expo: -8, | ||
publishTime: 1100 | ||
}); | ||
vm.expectRevert(abi.encodeWithSignature("DivisionByZero()")); | ||
harness.div(price1, price2); | ||
} | ||
|
||
function testAddSameExponent() public { | ||
PythStructs.Price memory price1 = PythStructs.Price({ | ||
price: 100, | ||
conf: 5, | ||
expo: -8, | ||
publishTime: 1000 | ||
}); | ||
PythStructs.Price memory price2 = PythStructs.Price({ | ||
price: 50, | ||
conf: 2, | ||
expo: -8, | ||
publishTime: 1100 | ||
}); | ||
PythStructs.Price memory result = harness.add(price1, price2); | ||
assertEq(result.price, 150, "Addition result should be 150"); | ||
assertEq(result.conf, 7, "Confidence should be summed"); | ||
assertEq(result.expo, -8, "Exponent should remain the same"); | ||
assertEq( | ||
result.publishTime, | ||
1000, | ||
"PublishTime should be the minimum of the two" | ||
); | ||
} | ||
|
||
function testAddDifferentExponent() public { | ||
PythStructs.Price memory price1 = PythStructs.Price({ | ||
price: 100, | ||
conf: 5, | ||
expo: -8, | ||
publishTime: 1000 | ||
}); | ||
PythStructs.Price memory price2 = PythStructs.Price({ | ||
price: 50, | ||
conf: 2, | ||
expo: -7, | ||
publishTime: 1100 | ||
}); | ||
vm.expectRevert(abi.encodeWithSignature("ExponentsMustMatch()")); | ||
harness.add(price1, price2); | ||
} | ||
|
||
function testMulNormalOperation() public { | ||
PythStructs.Price memory price1 = PythStructs.Price({ | ||
price: 100, | ||
conf: 5, | ||
expo: -8, | ||
publishTime: 1000 | ||
}); | ||
PythStructs.Price memory price2 = PythStructs.Price({ | ||
price: 50, | ||
conf: 2, | ||
expo: -8, | ||
publishTime: 1100 | ||
}); | ||
PythStructs.Price memory result = harness.mul(price1, price2); | ||
assertEq(result.price, 5000, "Multiplication result should be 5000"); | ||
assertEq(result.expo, -16, "Exponents should be added"); | ||
assertEq( | ||
result.publishTime, | ||
1000, | ||
"PublishTime should be the minimum of the two" | ||
); | ||
} | ||
|
||
function testNormalize() public { | ||
PythStructs.Price memory price = PythStructs.Price({ | ||
price: 2 * int64(PD_SCALE), | ||
conf: 3 * PD_SCALE, | ||
expo: 0, | ||
publishTime: 1000 | ||
}); | ||
PythStructs.Price memory result = harness.normalize(price); | ||
assertEq( | ||
result.price, | ||
(2 * int64(PD_SCALE)) / 100, | ||
"Price should be normalized to 10^9" | ||
); | ||
assertEq( | ||
result.conf, | ||
(3 * PD_SCALE) / 100, | ||
"Confidence should be normalized" | ||
); | ||
assertEq(result.expo, 2, "Exponent should be adjusted"); | ||
} | ||
|
||
function testScaleToExponentUpscale() public { | ||
PythStructs.Price memory price = PythStructs.Price({ | ||
price: 1000, | ||
conf: 50, | ||
expo: -8, | ||
publishTime: 1000 | ||
}); | ||
PythStructs.Price memory result = harness.scaleToExponent(price, -6); | ||
assertEq(result.price, 10, "Price should be scaled up"); | ||
assertEq( | ||
result.conf, | ||
0, | ||
"Confidence should be scaled (rounded down to 0)" | ||
); | ||
assertEq(result.expo, -6, "Exponent should be adjusted to target"); | ||
} | ||
|
||
function testToUnsignedPositive() public { | ||
(uint64 unsignedValue, int64 sign) = harness.toUnsigned(100); | ||
assertEq(unsignedValue, 100, "Unsigned value should be 100"); | ||
assertEq(sign, 1, "Sign should be positive"); | ||
} | ||
|
||
function testToUnsignedNegative() public { | ||
(uint64 unsignedValue, int64 sign) = harness.toUnsigned(-100); | ||
assertEq(unsignedValue, 100, "Unsigned value should be 100"); | ||
assertEq(sign, -1, "Sign should be negative"); | ||
} | ||
|
||
function testFraction() public { | ||
PythStructs.Price memory result = harness.fraction(10, 5); | ||
assertEq(result.price, 2 * 10 ** 9, "Fraction result should be 2"); | ||
assertEq(result.expo, -9, "Exponent should be -9"); | ||
} | ||
|
||
function testAffineCombination() public { | ||
PythStructs.Price memory y1 = PythStructs.Price({ | ||
price: 100, | ||
conf: 10, | ||
expo: -4, | ||
publishTime: 0 | ||
}); | ||
PythStructs.Price memory y2 = PythStructs.Price({ | ||
price: 100, | ||
conf: 10, | ||
expo: -4, | ||
publishTime: 0 | ||
}); | ||
PythStructs.Price memory result = harness.affineCombination( | ||
0, | ||
y1, | ||
10, | ||
y2, | ||
5, | ||
-9 | ||
); | ||
assertEq(result.price, 10 ** 7, "Affine combination should be 150"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this assert message is wrong. i think this test case is wrong too? should be 200 for y2, i think? |
||
assertEq(result.expo, -9, "Exponent should be -9"); | ||
} | ||
|
||
function testCmul() public { | ||
PythStructs.Price memory price = PythStructs.Price({ | ||
price: 100, | ||
conf: 5, | ||
expo: -8, | ||
publishTime: 1000 | ||
}); | ||
PythStructs.Price memory result = harness.cmul(price, 2, 0); | ||
assertEq(result.price, 200, "Multiplication result should be 200"); | ||
assertEq(result.expo, -8, "Exponent should remain the same"); | ||
} | ||
} | ||
|
||
contract PythPriceHarness { | ||
using PythPrice for *; | ||
|
||
function div( | ||
PythStructs.Price memory price, | ||
PythStructs.Price memory price2 | ||
) public pure returns (PythStructs.Price memory) { | ||
return PythPrice.div(price, price2); | ||
} | ||
|
||
function add( | ||
PythStructs.Price memory price, | ||
PythStructs.Price memory price2 | ||
) public pure returns (PythStructs.Price memory) { | ||
return PythPrice.add(price, price2); | ||
} | ||
|
||
function mul( | ||
PythStructs.Price memory price, | ||
PythStructs.Price memory price2 | ||
) public pure returns (PythStructs.Price memory) { | ||
return PythPrice.mul(price, price2); | ||
} | ||
|
||
function normalize( | ||
PythStructs.Price memory price | ||
) public pure returns (PythStructs.Price memory) { | ||
return PythPrice.normalize(price); | ||
} | ||
|
||
function scaleToExponent( | ||
PythStructs.Price memory price, | ||
int32 targetExpo | ||
) public pure returns (PythStructs.Price memory) { | ||
return PythPrice.scaleToExponent(price, targetExpo); | ||
} | ||
|
||
function toUnsigned(int64 x) public pure returns (uint64, int64) { | ||
return PythPrice.toUnsigned(x); | ||
} | ||
|
||
function fraction( | ||
int64 x, | ||
int64 y | ||
) public pure returns (PythStructs.Price memory) { | ||
return PythPrice.fraction(x, y); | ||
} | ||
|
||
function affineCombination( | ||
int64 x1, | ||
PythStructs.Price memory y1, | ||
int64 x2, | ||
PythStructs.Price memory y2, | ||
int64 x_query, | ||
int32 pre_add_expo | ||
) public pure returns (PythStructs.Price memory) { | ||
return | ||
PythPrice.affineCombination(x1, y1, x2, y2, x_query, pre_add_expo); | ||
} | ||
|
||
function cmul( | ||
PythStructs.Price memory price, | ||
int64 c, | ||
int32 e | ||
) public pure returns (PythStructs.Price memory) { | ||
return PythPrice.cmul(price, c, e); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert the confidence is as expected?