-
Notifications
You must be signed in to change notification settings - Fork 90
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
feat: add base parameter to /rates API #1527
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,64 @@ | ||
import { convert, Asset } from './util' | ||
|
||
describe('Rates util', function () { | ||
describe('convert', function () { | ||
it('converts same scales', () => { | ||
// Rate > 1 | ||
expect( | ||
convert({ | ||
exchangeRate: 1.5, | ||
sourceAmount: 100n, | ||
sourceAsset: asset(9), | ||
destinationAsset: asset(9) | ||
}) | ||
).toBe(150n) | ||
// Rate < 1 | ||
expect( | ||
convert({ | ||
exchangeRate: 0.5, | ||
sourceAmount: 100n, | ||
sourceAsset: asset(9), | ||
destinationAsset: asset(9) | ||
}) | ||
).toBe(50n) | ||
// Round down | ||
expect( | ||
convert({ | ||
exchangeRate: 0.5, | ||
sourceAmount: 101n, | ||
sourceAsset: asset(9), | ||
destinationAsset: asset(9) | ||
}) | ||
).toBe(50n) | ||
describe('Rates util', () => { | ||
describe('convert', () => { | ||
describe('convert same scales', () => { | ||
test.each` | ||
exchangeRate | sourceAmount | assetScale | expectedResult | description | ||
${1.5} | ${100n} | ${9} | ${150n} | ${'exchange rate above 1'} | ||
${1.1602} | ${12345n} | ${2} | ${14323n} | ${'exchange rate above 1 with rounding up'} | ||
${1.1602} | ${10001n} | ${2} | ${11603n} | ${'exchange rate above 1 with rounding down'} | ||
${0.5} | ${100n} | ${9} | ${50n} | ${'exchange rate below 1'} | ||
${0.5} | ${101n} | ${9} | ${51n} | ${'exchange rate below 1 with rounding up'} | ||
${0.8611} | ${1000n} | ${2} | ${861n} | ${'exchange rate below 1 with rounding down'} | ||
`( | ||
'$description', | ||
async ({ | ||
exchangeRate, | ||
sourceAmount, | ||
assetScale, | ||
expectedResult | ||
}): Promise<void> => { | ||
expect( | ||
convert({ | ||
exchangeRate, | ||
sourceAmount, | ||
sourceAsset: createAsset(assetScale), | ||
destinationAsset: createAsset(assetScale) | ||
}) | ||
).toBe(expectedResult) | ||
} | ||
) | ||
}) | ||
|
||
it('converts different scales', () => { | ||
// Scale low → high | ||
expect( | ||
convert({ | ||
exchangeRate: 1.5, | ||
sourceAmount: 100n, | ||
sourceAsset: asset(9), | ||
destinationAsset: asset(12) | ||
}) | ||
).toBe(150_000n) | ||
// Scale high → low | ||
expect( | ||
convert({ | ||
exchangeRate: 1.5, | ||
sourceAmount: 100_000n, | ||
sourceAsset: asset(12), | ||
destinationAsset: asset(9) | ||
}) | ||
).toBe(150n) | ||
describe('convert different scales', () => { | ||
test.each` | ||
exchangeRate | sourceAmount | sourceAssetScale | destinationAssetScale | expectedResult | description | ||
${1.5} | ${100n} | ${9} | ${12} | ${150_000n} | ${'convert scale from low to high'} | ||
${1.5} | ${100_000n} | ${12} | ${9} | ${150n} | ${'convert scale from high to low'} | ||
`( | ||
'$description', | ||
async ({ | ||
exchangeRate, | ||
sourceAmount, | ||
sourceAssetScale, | ||
destinationAssetScale, | ||
expectedResult | ||
}): Promise<void> => { | ||
expect( | ||
convert({ | ||
exchangeRate, | ||
sourceAmount, | ||
sourceAsset: createAsset(sourceAssetScale), | ||
destinationAsset: createAsset(destinationAssetScale) | ||
}) | ||
).toBe(expectedResult) | ||
} | ||
) | ||
}) | ||
}) | ||
}) | ||
|
||
function asset(scale: number): Asset { | ||
return { code: '[ignored]', scale } | ||
function createAsset(scale: number): Asset { | ||
return { code: 'XYZ', scale } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,11 +12,8 @@ export interface Asset { | |
} | ||
|
||
export function convert(opts: ConvertOptions): bigint { | ||
const maxScale = Math.max(opts.sourceAsset.scale, opts.destinationAsset.scale) | ||
const shiftUp = 10 ** maxScale | ||
const scaleDiff = opts.destinationAsset.scale - opts.sourceAsset.scale | ||
const scaledExchangeRate = opts.exchangeRate * 10 ** scaleDiff | ||
return ( | ||
(opts.sourceAmount * BigInt(scaledExchangeRate * shiftUp)) / BigInt(shiftUp) | ||
) | ||
|
||
return BigInt(Math.round(Number(opts.sourceAmount) * scaledExchangeRate)) | ||
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. BigInt divided by BigInt ends up truncating and not rounding the result. I think for currency, rounding up or down to the nearest unit makes more sense (and is standard). We can't really get around using 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. Do we need to be concerned about exceeding the 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. I'm quite certain we will never hit payment amounts over 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. We don't have any tooling right now for proper alerting/error management, and I feel like a log would just get swallowed. Going to merge this in for now. |
||
} |
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.
Rounding up now instead, see https://github.com/interledger/rafiki/pull/1527/files#diff-1eb7ceb504e87d9c310dc012970880799a92b90620ae8a3c2f7d40cafedfb2c0R12