diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 268bea8315..206c2ac336 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/infra", "description": "Infrastructure utilities for the Hyperlane Network", - "version": "3.1.2", + "version": "3.1.3", "dependencies": { "@arbitrum/sdk": "^3.0.0", "@aws-sdk/client-iam": "^3.74.0", diff --git a/typescript/infra/src/config/gas-oracle.ts b/typescript/infra/src/config/gas-oracle.ts index 41bf8ccac7..812314220a 100644 --- a/typescript/infra/src/config/gas-oracle.ts +++ b/typescript/infra/src/config/gas-oracle.ts @@ -1,7 +1,7 @@ import { BigNumber, ethers } from 'ethers'; import { ChainMap, ChainName } from '@hyperlane-xyz/sdk'; -import { convertDecimalsEthersBigNumber } from '@hyperlane-xyz/utils'; +import { convertDecimals } from '@hyperlane-xyz/utils'; import { mustGetChainNativeTokenDecimals } from '../utils/utils'; @@ -77,15 +77,17 @@ export function getTokenExchangeRateFromValues( localValue: BigNumber, remote: ChainName, remoteValue: BigNumber, -) { +): BigNumber { // This does not yet account for decimals! const exchangeRate = remoteValue .mul(TOKEN_EXCHANGE_RATE_MULTIPLIER) .div(localValue); - return convertDecimalsEthersBigNumber( - mustGetChainNativeTokenDecimals(remote), - mustGetChainNativeTokenDecimals(local), - exchangeRate, + return BigNumber.from( + convertDecimals( + mustGetChainNativeTokenDecimals(remote), + mustGetChainNativeTokenDecimals(local), + exchangeRate.toString(), + ), ); } diff --git a/typescript/sdk/logos/black/base.svg b/typescript/sdk/logos/black/base.svg new file mode 100644 index 0000000000..be9b3c0889 --- /dev/null +++ b/typescript/sdk/logos/black/base.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/black/polygonzkevm.svg b/typescript/sdk/logos/black/polygonzkevm.svg new file mode 100644 index 0000000000..2457c2bfbc --- /dev/null +++ b/typescript/sdk/logos/black/polygonzkevm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/black/scroll.svg b/typescript/sdk/logos/black/scroll.svg new file mode 100644 index 0000000000..77e7199a9d --- /dev/null +++ b/typescript/sdk/logos/black/scroll.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/color/base.svg b/typescript/sdk/logos/color/base.svg new file mode 100644 index 0000000000..59182e5dd7 --- /dev/null +++ b/typescript/sdk/logos/color/base.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/color/polygonzkevm.svg b/typescript/sdk/logos/color/polygonzkevm.svg new file mode 100644 index 0000000000..98cca40919 --- /dev/null +++ b/typescript/sdk/logos/color/polygonzkevm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/color/scroll.svg b/typescript/sdk/logos/color/scroll.svg new file mode 100644 index 0000000000..541dc4433f --- /dev/null +++ b/typescript/sdk/logos/color/scroll.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/utils/index.ts b/typescript/utils/index.ts index 35e81292dd..08abee53a1 100644 --- a/typescript/utils/index.ts +++ b/typescript/utils/index.ts @@ -39,7 +39,6 @@ export { } from './src/addresses'; export { convertDecimals, - convertDecimalsEthersBigNumber, eqAmountApproximate, fromWei, fromWeiRounded, diff --git a/typescript/utils/package.json b/typescript/utils/package.json index 88e09f1640..4d3947371f 100644 --- a/typescript/utils/package.json +++ b/typescript/utils/package.json @@ -29,7 +29,7 @@ "clean": "rm -rf ./dist", "check": "tsc --noEmit", "prettier": "prettier --write ./src", - "test:unit": "mocha --config .mocharc.json './src/**/*.test.ts'" + "test": "mocha --config .mocharc.json './src/**/*.test.ts'" }, "sideEffects": false, "types": "dist/index.d.ts", diff --git a/typescript/utils/src/amount.test.ts b/typescript/utils/src/amount.test.ts new file mode 100644 index 0000000000..4ba6c9962a --- /dev/null +++ b/typescript/utils/src/amount.test.ts @@ -0,0 +1,54 @@ +import { expect } from 'chai'; + +import { eqAmountApproximate, fromWei, fromWeiRounded, toWei } from './amount'; + +describe('fromWei', () => { + it('parses and converts correctly', () => { + expect(fromWei(1, 0)).to.equal('1'); + expect(fromWei('1000000', 6)).to.equal('1'); + expect(fromWei('1000000000000000000')).to.equal('1'); + expect(fromWei('1000000000000000000.1234')).to.equal('1'); + }); +}); + +describe('fromWeiRounded', () => { + it('parses and converts correctly', () => { + expect(fromWeiRounded(1, 0)).to.equal('1.0000'); + expect(fromWeiRounded('1000000', 6)).to.equal('1.0000'); + expect(fromWeiRounded('1000000000000000000')).to.equal('1.0000'); + expect(fromWeiRounded('1000000000000000000.1234')).to.equal('1.0000'); + }); + + it('rounds correctly', () => { + expect(fromWeiRounded(1234567890, 6, 2)).to.equal('1234.56'); + expect(fromWeiRounded('1234567890', 6, 4)).to.equal('1234.5678'); + expect(fromWeiRounded('10000000000000000000')).to.equal('10.0000'); + expect(fromWeiRounded('10000000000000000000', 18, 0)).to.equal('10'); + }); + + it('can drop decimals for large numbers', () => { + expect(fromWeiRounded('10001000000000000000000')).to.equal('10001.00'); + expect(fromWeiRounded('10001000000000000000', 15, 4)).to.equal( + '10001.0000', + ); + }); +}); + +describe('toWei', () => { + it('parses and converts correctly', () => { + expect(toWei(1, 0)).to.equal('1'); + expect(toWei('1', 6)).to.equal('1000000'); + expect(toWei('123.456')).to.equal('123456000000000000000'); + expect(toWei('1.00000000000000000001')).to.equal('1000000000000000000'); + expect(toWei('1.00000000000000000001', 6)).to.equal('1000000'); + }); +}); + +describe('eqAmountApproximate', () => { + it('compares correctly', () => { + expect(eqAmountApproximate(1, 1.001, 0.001)).to.be.true; + expect(eqAmountApproximate(9, 9.001, 0.01)).to.be.true; + expect(eqAmountApproximate('9876543210', '9876543210', '1')).to.be.true; + expect(eqAmountApproximate('9876543210', '9876543212', '1')).to.be.false; + }); +}); diff --git a/typescript/utils/src/amount.ts b/typescript/utils/src/amount.ts index 5f4dc83390..f46998230e 100644 --- a/typescript/utils/src/amount.ts +++ b/typescript/utils/src/amount.ts @@ -1,14 +1,9 @@ import { formatUnits, parseUnits } from '@ethersproject/units'; import BigNumber from 'bignumber.js'; -import { ethers } from 'ethers'; -const DEFAULT_MIN_ROUNDED_VALUE = 0.00001; const DEFAULT_DISPLAY_DECIMALS = 4; const DEFAULT_TOKEN_DECIMALS = 18; -// Use toString(10) on bignumber.js to prevent ethers.js bigNumber error -// when parsing exponential string over e21 - /** * Convert the given Wei value to Ether value * @param value The value to convert. @@ -34,21 +29,14 @@ export function fromWei( export function fromWeiRounded( value: BigNumber.Value | null | undefined, decimals = DEFAULT_TOKEN_DECIMALS, - roundDownIfSmall = true, + displayDecimals?: number, ): string { if (!value) return '0'; const flooredValue = BigNumber(value).toFixed(0, BigNumber.ROUND_FLOOR); const amount = BigNumber(formatUnits(flooredValue, decimals)); if (amount.isZero()) return '0'; - - // If amount is less than min value - if (amount.lt(DEFAULT_MIN_ROUNDED_VALUE)) { - if (roundDownIfSmall) return '0'; - return amount.toString(10); - } - - const displayDecimals = amount.gte(10000) ? 2 : DEFAULT_DISPLAY_DECIMALS; - return amount.toFixed(displayDecimals); + displayDecimals ??= amount.gte(10000) ? 2 : DEFAULT_DISPLAY_DECIMALS; + return amount.toFixed(displayDecimals, BigNumber.ROUND_FLOOR); } /** @@ -101,17 +89,17 @@ export function tryParseAmount( /** * Checks if an amount is equal of nearly equal to balance within a small margin of error * Necessary because amounts in the UI are often rounded - * @param amountInWei1 The amount to compare. - * @param amountInWei2 The amount to compare. + * @param amount1 The amount to compare. + * @param amount2 The amount to compare. * @returns true/false. */ export function eqAmountApproximate( - amountInWei1: BigNumber.Value, - amountInWei2: BigNumber.Value, + amount1: BigNumber.Value, + amount2: BigNumber.Value, + maxDifference: BigNumber.Value, ): boolean { - const minValueWei = toWei(DEFAULT_MIN_ROUNDED_VALUE); - // Is difference btwn amount and balance less than min amount shown for token - return BigNumber(amountInWei1).minus(amountInWei2).abs().lt(minValueWei); + // Is difference btwn amounts less than maxDifference + return BigNumber(amount1).minus(amount2).abs().lte(maxDifference); } /** @@ -143,28 +131,3 @@ export function convertDecimals( return amount.times(BigNumber(10).pow(difference)).toString(10); } } - -/** - * Converts a value with `fromDecimals` decimals to a value with `toDecimals` decimals. - * Incurs a loss of precision when `fromDecimals` > `toDecimals`. - * @param fromDecimals The number of decimals `value` has. - * @param toDecimals The number of decimals to convert `value` to. - * @param value The value to convert. - * @returns `value` represented with `toDecimals` decimals. - */ -export function convertDecimalsEthersBigNumber( - fromDecimals: number, - toDecimals: number, - value: ethers.BigNumber, -) { - if (fromDecimals === toDecimals) return value; - else if (fromDecimals > toDecimals) { - const difference = fromDecimals - toDecimals; - return value.div(ethers.BigNumber.from('10').pow(difference)); - } - // fromDecimals < toDecimals - else { - const difference = toDecimals - fromDecimals; - return value.mul(ethers.BigNumber.from('10').pow(difference)); - } -} diff --git a/typescript/utils/src/big-numbers.test.ts b/typescript/utils/src/big-numbers.test.ts index 1de260be67..82efee036d 100644 --- a/typescript/utils/src/big-numbers.test.ts +++ b/typescript/utils/src/big-numbers.test.ts @@ -12,109 +12,107 @@ import { mulBigAndFixed, } from './big-numbers'; -describe('utils', () => { - describe('isBigNumberish', () => { - const testCases = [ - { expect: false, context: 'invalid number', case: 'invalidNumber' }, - { expect: false, context: 'NaN', case: NaN }, - { expect: false, context: 'undefined', case: undefined }, - { expect: false, context: 'null', case: null }, - { expect: true, context: 'decimal', case: 123.123 }, - { expect: true, context: 'integer', case: 300_000 }, - { expect: true, context: 'hex 0', case: 0x00 }, - { expect: true, context: 'hex 0', case: 0x000 }, - { - expect: true, - context: 'address 0', - case: 0x0000000000000000000000000000000000000000, - }, - ]; - testCases.forEach((tc) => { - it(`returns ${tc.expect} for ${tc.case}`, () => { - expect(isBigNumberish(tc.case!)).to.equal(tc.expect); - }); +describe('isBigNumberish', () => { + const testCases = [ + { expect: false, context: 'invalid number', case: 'invalidNumber' }, + { expect: false, context: 'NaN', case: NaN }, + { expect: false, context: 'undefined', case: undefined }, + { expect: false, context: 'null', case: null }, + { expect: true, context: 'decimal', case: 123.123 }, + { expect: true, context: 'integer', case: 300_000 }, + { expect: true, context: 'hex 0', case: 0x00 }, + { expect: true, context: 'hex 0', case: 0x000 }, + { + expect: true, + context: 'address 0', + case: 0x0000000000000000000000000000000000000000, + }, + ]; + testCases.forEach((tc) => { + it(`returns ${tc.expect} for ${tc.case}`, () => { + expect(isBigNumberish(tc.case!)).to.equal(tc.expect); }); }); +}); - describe('isZeroish', () => { - const testCases = [ - { expect: false, context: 'invalid number', case: 'invalidNumber' }, - { expect: false, context: 'NaN', case: NaN }, - { expect: false, context: 'undefined', case: undefined }, - { expect: false, context: 'null', case: null }, - { expect: false, context: 'non 0 decimal', case: 123.123 }, - { expect: false, context: 'non 0 integer', case: 123 }, - { expect: true, context: 'hex 0', case: 0x00 }, - { expect: true, context: 'hex 0', case: 0x000 }, - { - expect: true, - context: 'address 0', - case: 0x0000000000000000000000000000000000000000, - }, - ]; - testCases.forEach((tc) => { - it(`returns ${tc.expect} for ${tc.case}`, () => { - expect(isZeroish(tc.case!)).to.equal(tc.expect); - }); +describe('isZeroish', () => { + const testCases = [ + { expect: false, context: 'invalid number', case: 'invalidNumber' }, + { expect: false, context: 'NaN', case: NaN }, + { expect: false, context: 'undefined', case: undefined }, + { expect: false, context: 'null', case: null }, + { expect: false, context: 'non 0 decimal', case: 123.123 }, + { expect: false, context: 'non 0 integer', case: 123 }, + { expect: true, context: 'hex 0', case: 0x00 }, + { expect: true, context: 'hex 0', case: 0x000 }, + { + expect: true, + context: 'address 0', + case: 0x0000000000000000000000000000000000000000, + }, + ]; + testCases.forEach((tc) => { + it(`returns ${tc.expect} for ${tc.case}`, () => { + expect(isZeroish(tc.case!)).to.equal(tc.expect); }); }); +}); - describe('bigToFixed', () => { - it('converts a BigNumber to a FixedNumber', () => { - const big = BigNumber('7.5e-10'); - const fixed = bigToFixed(big); +describe('bigToFixed', () => { + it('converts a BigNumber to a FixedNumber', () => { + const big = BigNumber('7.5e-10'); + const fixed = bigToFixed(big); - expect(fixed.toUnsafeFloat()).to.equal(7.5e-10); - }); + expect(fixed.toUnsafeFloat()).to.equal(7.5e-10); }); +}); - describe('fixedToBig', () => { - it('converts a FixedNumber to a floored BigNumber', () => { - const fixed = FixedNumber.from('12.34'); - const big = fixedToBig(fixed); +describe('fixedToBig', () => { + it('converts a FixedNumber to a floored BigNumber', () => { + const fixed = FixedNumber.from('12.34'); + const big = fixedToBig(fixed); - expect(big.toNumber()).to.equal(12); - }); + expect(big.toNumber()).to.equal(12); + }); - it('converts a FixedNumber to a ceilinged BigNumber', () => { - const fixed = FixedNumber.from('12.34'); - const big = fixedToBig(fixed, true); + it('converts a FixedNumber to a ceilinged BigNumber', () => { + const fixed = FixedNumber.from('12.34'); + const big = fixedToBig(fixed, true); - expect(big.toNumber()).to.equal(13); - }); + expect(big.toNumber()).to.equal(13); }); +}); - describe('mulBigAndFixed', () => { - it('gets the floored product of a BigNumber and FixedNumber', () => { - const big = BigNumber('1000'); - const fixed = FixedNumber.from('1.2345'); - const product = mulBigAndFixed(big, fixed); +describe('mulBigAndFixed', () => { + it('gets the floored product of a BigNumber and FixedNumber', () => { + const big = BigNumber('1000'); + const fixed = FixedNumber.from('1.2345'); + const product = mulBigAndFixed(big, fixed); - expect(product).to.equal((1234).toString()); - }); + expect(product).to.equal((1234).toString()); + }); - it('gets the ceilinged product of a BigNumber and FixedNumber', () => { - const big = BigNumber('1000'); - const fixed = FixedNumber.from('1.2345'); - const product = mulBigAndFixed(big, fixed, true); + it('gets the ceilinged product of a BigNumber and FixedNumber', () => { + const big = BigNumber('1000'); + const fixed = FixedNumber.from('1.2345'); + const product = mulBigAndFixed(big, fixed, true); - expect(product).to.equal((1235).toString()); - }); + expect(product).to.equal((1235).toString()); }); +}); - describe('BigNumberMin', () => { - it('gets the min between the two BigNumber', () => { - const big = BigNumber('1000'); - const bigger = BigNumber('10000'); - expect(BigNumberMin(big, bigger)).to.equal(big.toString()); - }); +describe('BigNumberMin', () => { + it('gets the min between the two BigNumber', () => { + const big = BigNumber('1000'); + const bigger = BigNumber('10000'); + expect(BigNumberMin(big, bigger)).to.equal(big.toString()); }); +}); - describe('BigNumberMax', () => { - it('gets the max between the two BigNumber', () => { - const big = BigNumber('1000'); - const bigger = BigNumber('10000'); - expect(BigNumberMax(big, bigger)).to.equal(bigger.toString()); - }); +describe('BigNumberMax', () => { + it('gets the max between the two BigNumber', () => { + const big = BigNumber('1000'); + const bigger = BigNumber('10000'); + expect(BigNumberMax(big, bigger)).to.equal(bigger.toString()); }); });