Skip to content

Commit

Permalink
Remove bigint literals
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmillr committed Nov 23, 2024
1 parent bbf3a3b commit 3e481ca
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 61 deletions.
10 changes: 7 additions & 3 deletions src/abi/kyber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const _ABI = [
{type:"function",name:"getExpectedRate",inputs:[{name:"src",type:"address"},{name:"dest",type:"address"},{name:"srcQty",type:"uint256"}],outputs:[{name:"expectedRate",type:"uint256"},{name:"worstRate",type:"uint256"}]},{type:"function",name:"getExpectedRateAfterFee",inputs:[{name:"src",type:"address"},{name:"dest",type:"address"},{name:"srcQty",type:"uint256"},{name:"platformFeeBps",type:"uint256"},{name:"hint",type:"bytes"}],outputs:[{name:"expectedRate",type:"uint256"}]},{type:"function",name:"trade",inputs:[{name:"src",type:"address"},{name:"srcAmount",type:"uint256"},{name:"dest",type:"address"},{name:"destAddress",type:"address"},{name:"maxDestAmount",type:"uint256"},{name:"minConversionRate",type:"uint256"},{name:"platformWallet",type:"address"}],outputs:[{type:"uint256"}]},{type:"function",name:"tradeWithHint",inputs:[{name:"src",type:"address"},{name:"srcAmount",type:"uint256"},{name:"dest",type:"address"},{name:"destAddress",type:"address"},{name:"maxDestAmount",type:"uint256"},{name:"minConversionRate",type:"uint256"},{name:"walletId",type:"address"},{name:"hint",type:"bytes"}],outputs:[{type:"uint256"}]},{type:"function",name:"tradeWithHintAndFee",inputs:[{name:"src",type:"address"},{name:"srcAmount",type:"uint256"},{name:"dest",type:"address"},{name:"destAddress",type:"address"},{name:"maxDestAmount",type:"uint256"},{name:"minConversionRate",type:"uint256"},{name:"platformWallet",type:"address"},{name:"platformFeeBps",type:"uint256"},{name:"hint",type:"bytes"}],outputs:[{name:"destAmount",type:"uint256"}]}
] as const;

const _10n = BigInt(10);
const hints = {
tradeWithHintAndFee(v: any, opt: HintOpt) {
if (!opt.contracts) throw Error('Not enough info');
Expand All @@ -21,9 +22,12 @@ const hints = {
const destAmount =
((v.srcAmount as bigint) *
(v.minConversionRate as bigint) *
10n ** BigInt(destInfo.decimals!)) /
10n ** (BigInt(srcInfo.decimals!) + 18n);
const fee = formatToken((BigInt(v.platformFeeBps) * BigInt(v.srcAmount)) / 10000n, srcInfo);
_10n ** BigInt(destInfo.decimals!)) /
_10n ** (BigInt(srcInfo.decimals!) + BigInt(18));
const fee = formatToken(
(BigInt(v.platformFeeBps) * BigInt(v.srcAmount)) / BigInt(10000),
srcInfo
);
return `Swap ${formatToken(v.srcAmount, srcInfo)} For ${formatToken(
destAmount,
destInfo
Expand Down
11 changes: 6 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ export const authorization = {
const DEFAULTS = {
accessList: [], // needs to be .slice()-d to create new reference
authorizationList: [],
chainId: 1n, // mainnet
chainId: BigInt(1), // mainnet
data: '',
gasLimit: 21000n, // TODO: investigate if limit is smaller in eip4844 txs
maxPriorityFeePerGas: 1n * amounts.GWEI, // Reduce fingerprinting by using standard, popular value
gasLimit: BigInt(21000), // TODO: investigate if limit is smaller in eip4844 txs
maxPriorityFeePerGas: BigInt(1) * amounts.GWEI, // Reduce fingerprinting by using standard, popular value
type: 'eip1559',
} as const;
type DefaultField = keyof typeof DEFAULTS;
Expand Down Expand Up @@ -150,11 +150,12 @@ export class Transaction<T extends TxType> {
* @returns new transaction with adjusted amounts
*/
setWholeAmount(accountBalance: bigint, burnRemaining = true): Transaction<T> {
if (typeof accountBalance !== 'bigint' || accountBalance <= 0n)
const _0n = BigInt(0);
if (typeof accountBalance !== 'bigint' || accountBalance <= _0n)
throw new Error('account balance must be bigger than 0');
const fee = this.fee;
const amountToSend = accountBalance - fee;
if (amountToSend <= 0n) throw new Error('account balance must be bigger than fee of ' + fee);
if (amountToSend <= _0n) throw new Error('account balance must be bigger than fee of ' + fee);
const raw = { ...this.raw, value: amountToSend };
if (!['legacy', 'eip2930'].includes(this.type) && burnRemaining) {
const r = raw as SpecifyVersionNeg<['legacy', 'eip2930']>;
Expand Down
9 changes: 5 additions & 4 deletions src/net/archive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,23 +665,24 @@ export class Web3Provider implements IWeb3Provider {
export function calcTransfersDiff(transfers: TxTransfers[]): (TxTransfers & Balances)[] {
const balances: Record<string, bigint> = {};
const tokenBalances: Record<string, Record<string, bigint>> = {};
let _0 = BigInt(0);
for (const t of transfers) {
for (const it of t.transfers) {
if (it.from) {
if (balances[it.from] === undefined) balances[it.from] = 0n;
if (balances[it.from] === undefined) balances[it.from] = _0;
balances[it.from] -= it.value;
}
if (it.to) {
if (balances[it.to] === undefined) balances[it.to] = 0n;
if (balances[it.to] === undefined) balances[it.to] = _0;
balances[it.to] += it.value;
}
}
for (const tt of t.tokenTransfers) {
if (!tokenBalances[tt.contract]) tokenBalances[tt.contract] = {};
const token = tokenBalances[tt.contract];
if (token[tt.from] === undefined) token[tt.from] = 0n;
if (token[tt.from] === undefined) token[tt.from] = _0;
token[tt.from] -= tt.value;
if (token[tt.to] === undefined) token[tt.to] = 0n;
if (token[tt.to] === undefined) token[tt.to] = _0;
token[tt.to] += tt.value;
}
Object.assign(t, {
Expand Down
2 changes: 1 addition & 1 deletion src/net/uniswap-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export type SwapElm = {

export function addPercent(n: bigint, _perc: number) {
const perc = BigInt((_perc * 10000) | 0);
const p100 = 100n * 10000n;
const p100 = BigInt(100) * BigInt(10000);
return ((p100 + perc) * n) / p100;
}

Expand Down
12 changes: 7 additions & 5 deletions src/net/uniswap-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,15 @@ export function amount(
if (!reserveIn || !reserveOut || (amountOut && amountOut >= reserveOut))
throw new Error('Uniswap: Insufficient reserves');
if (amountIn) {
const amountInWithFee = amountIn * 997n;
const amountOut = (amountInWithFee * reserveOut) / (reserveIn * 1000n + amountInWithFee);
if (amountOut === 0n || amountOut >= reserveOut)
const amountInWithFee = amountIn * BigInt(997);
const amountOut = (amountInWithFee * reserveOut) / (reserveIn * BigInt(1000) + amountInWithFee);
if (amountOut === BigInt(0) || amountOut >= reserveOut)
throw new Error('Uniswap: Insufficient reserves');
return amountOut;
} else if (amountOut)
return (reserveIn * amountOut * 1000n) / ((reserveOut - amountOut) * 997n) + 1n;
return (
(reserveIn * amountOut * BigInt(1000)) / ((reserveOut - amountOut) * BigInt(997)) + BigInt(1)
);
else throw new Error('uniswap.amount: provide only one amount');
}

Expand Down Expand Up @@ -172,7 +174,7 @@ export function txData(
path: path.path,
});
const amount = amountIn ? amountIn : amountInMax;
const value = input === 'eth' ? amount : 0n;
const value = input === 'eth' ? amount : BigInt(0);
const allowance = input === 'eth' ? undefined : { token: input, amount };
return { to: UNISWAP_V2_ROUTER_CONTRACT, value, data, allowance };
}
Expand Down
4 changes: 2 additions & 2 deletions src/net/uniswap-v3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export function txData(
deadline,
amountIn: (amountIn || route.amountIn) as bigint,
amountOut: (amountOut || route.amountOut) as bigint,
sqrtPriceLimitX96: opt.sqrtPriceLimitX96 || 0n,
sqrtPriceLimitX96: opt.sqrtPriceLimitX96 || BigInt(0),
amountInMaximum: undefined as bigint | undefined,
amountOutMinimum: undefined as bigint | undefined,
};
Expand Down Expand Up @@ -187,7 +187,7 @@ export function txData(
}
const data =
calldatas.length === 1 ? calldatas[0] : ROUTER_CONTRACT['multicall'].encodeInput(calldatas);
const value = input === 'eth' ? (amountIn ? amountIn : args.amountInMaximum) : 0n;
const value = input === 'eth' ? (amountIn ? amountIn : args.amountInMaximum) : BigInt(0);
const allowance =
input !== 'eth'
? { token: input, amount: amountIn ? amountIn : args.amountInMaximum }
Expand Down
3 changes: 2 additions & 1 deletion src/rlp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ export const RLP = P.apply(rlpInner, {
if (data === 0) return this.decode(empty);
return this.decode(numberToVarBytesBE(data));
case 'bigint':
if (data < 0n) throw new Error('RLP.encode: invalid integer as argument, must be unsigned');
if (data < BigInt(0))
throw new Error('RLP.encode: invalid integer as argument, must be unsigned');
return this.decode(numberToVarBytesBE(data));
case 'string':
return this.decode(data.startsWith('0x') ? phex.encode(data) : pstr.encode(data));
Expand Down
7 changes: 4 additions & 3 deletions src/ssz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,13 @@ const int = (len: number, small = true) =>
},
});

const _0n = BigInt(0);
export const uint8 = basic('uint8', int(1), 0);
export const uint16 = basic('uint16', int(2), 0);
export const uint32 = basic('uint32', int(4), 0);
export const uint64 = basic('uint64', int(8, false), 0n);
export const uint128 = basic('uint128', int(16, false), 0n);
export const uint256 = basic('uint256', int(32, false), 0n);
export const uint64 = basic('uint64', int(8, false), _0n);
export const uint128 = basic('uint128', int(16, false), _0n);
export const uint256 = basic('uint256', int(32, false), _0n);
export const boolean = basic('boolean', P.bool, false);

const array = <T>(len: P.Length, inner: SSZCoder<T>): P.CoderType<T[]> => {
Expand Down
46 changes: 25 additions & 21 deletions src/tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { isObject, amounts, ethHex, isBytes } from './utils.js';

// Transaction parsers

const _0n = BigInt(0);

export type AnyCoder = Record<string, P.Coder<any, any>>;
export type AnyCoderStream = Record<string, P.CoderType<any>>;

Expand Down Expand Up @@ -55,7 +57,7 @@ const createTxMap = <T extends AnyCoderStream>(versions: T): P.CoderType<Version
* - optional fields change the length of underlying array
*/
const isOptBig = (a: unknown) => a === undefined || typeof a === 'bigint';
const isNullOr0 = (a: unknown) => a === undefined || a === 0n;
const isNullOr0 = (a: unknown) => a === undefined || a === BigInt(0);

function assertYParityValid(elm: number) {
// TODO: is this correct? elm = 0 default?
Expand Down Expand Up @@ -90,12 +92,12 @@ export const legacySig = {
if (v === undefined) return { chainId: undefined };
// TODO: handle (invalid?) negative v
if (typeof v !== 'bigint') throw new Error(`invalid v type=${typeof v}`);
if ((r === undefined && s === undefined) || (r === 0n && s === 0n)) return { chainId: v };
if (v === 27n) return { yParity: 0, chainId: undefined, r, s };
if (v === 28n) return { yParity: 1, chainId: undefined, r, s };
if (v < 35n) throw new Error(`wrong v=${v}`);
const v2 = v - 35n;
return { chainId: v2 >> 1n, yParity: Number(v2 & 1n), r, s };
if ((r === undefined && s === undefined) || (r === _0n && s === _0n)) return { chainId: v };
if (v === BigInt(27)) return { yParity: 0, chainId: undefined, r, s };
if (v === BigInt(28)) return { yParity: 1, chainId: undefined, r, s };
if (v < BigInt(35)) throw new Error(`wrong v=${v}`);
const v2 = v - BigInt(35);
return { chainId: v2 >> BigInt(1), yParity: Number(v2 & BigInt(1)), r, s };
},
decode: (data: YRS) => {
aobj(data);
Expand All @@ -107,20 +109,22 @@ export const legacySig = {
throw new Error(`wrong yParity type=${typeof chainId}`);
if (yParity === undefined) {
if (chainId !== undefined) {
if ((r !== undefined && r !== 0n) || (s !== undefined && s !== 0n))
if ((r !== undefined && r !== _0n) || (s !== undefined && s !== _0n))
throw new Error(`wrong unsigned legacy r=${r} s=${s}`);
return { v: chainId, r: 0n, s: 0n };
return { v: chainId, r: _0n, s: _0n };
}
// no parity, chainId, but r, s exists
if ((r !== undefined && r !== 0n) || (s !== undefined && s !== 0n))
if ((r !== undefined && r !== _0n) || (s !== undefined && s !== _0n))
throw new Error(`wrong unsigned legacy r=${r} s=${s}`);
return {};
}
// parity exists, which means r & s should exist too!
if (isNullOr0(r) || isNullOr0(s)) throw new Error(`wrong unsigned legacy r=${r} s=${s}`);
assertYParityValid(yParity);
const v =
chainId !== undefined ? BigInt(yParity) + (chainId * 2n + 35n) : BigInt(yParity) + 27n;
chainId !== undefined
? BigInt(yParity) + (chainId * BigInt(2) + BigInt(35))
: BigInt(yParity) + BigInt(27);
return { v, r, s };
},
} as P.Coder<VRS, YRS>;
Expand Down Expand Up @@ -413,26 +417,26 @@ type ValidationOpts = { strict: boolean; type: TxType; data: Record<string, any>
const validators: Record<string, (num: any, { strict, type, data }: ValidationOpts) => void> = {
nonce(num: bigint, { strict }: ValidationOpts) {
abig(num);
if (strict) minmax(num, 0n, amounts.maxNonce);
else minmax(BigInt(num), 0n, BigInt(Number.MAX_SAFE_INTEGER)); // amounts.maxUint64
if (strict) minmax(num, _0n, amounts.maxNonce);
else minmax(BigInt(num), _0n, BigInt(Number.MAX_SAFE_INTEGER)); // amounts.maxUint64
},
maxFeePerGas(num: bigint, { strict }: ValidationOpts) {
abig(num);
if (strict) minmax(num, 1n, amounts.maxGasPrice, '>= 1 wei and < 10000 gwei');
else minmax(num, 0n, amounts.maxUint64);
if (strict) minmax(num, BigInt(1), amounts.maxGasPrice, '>= 1 wei and < 10000 gwei');
else minmax(num, _0n, amounts.maxUint64);
},
maxPriorityFeePerGas(num: bigint, { strict, data }: ValidationOpts) {
abig(num);
if (strict) minmax(num, 0n, amounts.maxGasPrice, '>= 1 wei and < 10000 gwei');
else minmax(num, 0n, amounts.maxUint64, '>= 1 wei and < 10000 gwei');
if (strict) minmax(num, _0n, amounts.maxGasPrice, '>= 1 wei and < 10000 gwei');
else minmax(num, _0n, amounts.maxUint64, '>= 1 wei and < 10000 gwei');
if (strict && data && typeof data.maxFeePerGas === 'bigint' && data.maxFeePerGas < num) {
throw new Error(`cannot be bigger than maxFeePerGas=${data.maxFeePerGas}`);
}
},
gasLimit(num: bigint, { strict }: ValidationOpts) {
abig(num);
if (strict) minmax(num, amounts.minGasLimit, amounts.maxGasLimit);
else minmax(num, 0n, amounts.maxUint64);
else minmax(num, _0n, amounts.maxUint64);
},
to(address: string, { strict, data }: ValidationOpts) {
if (!addr.isValid(address, true)) throw new Error('address checksum does not match');
Expand All @@ -441,7 +445,7 @@ const validators: Record<string, (num: any, { strict, type, data }: ValidationOp
},
value(num: bigint) {
abig(num);
minmax(num, 0n, amounts.maxAmount, '>= 0 and < 100M eth');
minmax(num, _0n, amounts.maxAmount, '>= 0 and < 100M eth');
},
data(val: string, { strict, data }: ValidationOpts) {
if (typeof val !== 'string') throw new Error('data must be string');
Expand All @@ -456,7 +460,7 @@ const validators: Record<string, (num: any, { strict, type, data }: ValidationOp
// chainId is optional for legacy transactions
if (type === 'legacy' && num === undefined) return;
abig(num);
if (strict) minmax(num, 1n, amounts.maxChainId, '>= 1 and <= 2**32-1');
if (strict) minmax(num, BigInt(1), amounts.maxChainId, '>= 1 and <= 2**32-1');
},
accessList(list: AccessList) {
// NOTE: we cannot handle this validation in coder, since it requires chainId to calculate correct checksum
Expand All @@ -469,7 +473,7 @@ const validators: Record<string, (num: any, { strict, type, data }: ValidationOp
if (!addr.isValid(address)) throw new Error('address checksum does not match');
// chainId in authorization list can be zero (==allow any chain)
abig(chainId);
if (opts.strict) minmax(chainId, 0n, amounts.maxChainId, '>= 0 and <= 2**32-1');
if (opts.strict) minmax(chainId, _0n, amounts.maxChainId, '>= 0 and <= 2**32-1');
this.nonce(nonce, opts);
}
},
Expand Down
32 changes: 16 additions & 16 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,24 @@ export type IWeb3Provider = {

const ETH_PRECISION = 18;
const GWEI_PRECISION = 9;
const GWEI = 10n ** BigInt(GWEI_PRECISION);
const ETHER = 10n ** BigInt(ETH_PRECISION);
const GWEI = BigInt(10) ** BigInt(GWEI_PRECISION);
const ETHER = BigInt(10) ** BigInt(ETH_PRECISION);
export const amounts = /* @__PURE__ */ (() => ({
GWEI_PRECISION,
ETH_PRECISION,
GWEI,
ETHER,
// Disabled with "strict=false"
maxAmount: 1_000_000n * ETHER, // 1M ether for testnets
minGasLimit: 21_000n, // 21K wei is used at minimum. Possibly smaller gas limit in 4844 txs?
maxGasLimit: 30_000_000n, // 30M wei. A block limit in 2024 is 30M
maxGasPrice: 10_000n * GWEI, // 10K gwei. Arbitrage HFT bots can use more
maxNonce: 131_072n, // 2**17, but in spec it's actually 2**64-1
maxAmount: BigInt(1_000_000) * ETHER, // 1M ether for testnets
minGasLimit: BigInt(21_000), // 21K wei is used at minimum. Possibly smaller gas limit in 4844 txs?
maxGasLimit: BigInt(30_000_000), // 30M wei. A block limit in 2024 is 30M
maxGasPrice: BigInt(10_000) * GWEI, // 10K gwei. Arbitrage HFT bots can use more
maxNonce: BigInt(131_072), // 2**17, but in spec it's actually 2**64-1
maxDataSize: 1_000_000, // Size of .data field. TODO: research
maxInitDataSize: 49_152, // EIP-3860
maxChainId: BigInt(2 ** 32 - 1),
maxUint64: 2n ** 64n - 1n,
maxUint256: 2n ** 256n - 1n,
maxUint64: BigInt(2) ** BigInt(64) - BigInt(1),
maxUint256: BigInt(2) ** BigInt(256) - BigInt(1),
}))();

// For usage with other packed utils via apply
Expand All @@ -54,7 +54,7 @@ export const amounts = /* @__PURE__ */ (() => ({
// even 'data' can be '0x'
//
// 0x data = Uint8Array([])
// 0x num = 0n
// 0x num = BigInt(0)
const leadingZerosRe = /^0+/;
const genEthHex = (keepLeadingZero = true): Coder<Uint8Array, string> => ({
decode: (data: string): Uint8Array => {
Expand Down Expand Up @@ -89,7 +89,7 @@ export function numberTo0xHex(num: number | bigint): string {

export function hexToNumber(hex: string): bigint {
if (typeof hex !== 'string') throw new TypeError('expected hex string, got ' + typeof hex);
return hex ? BigInt(add0x(hex)) : 0n;
return hex ? BigInt(add0x(hex)) : BigInt(0);
}

export function isObject(item: unknown): item is Record<string, any> {
Expand All @@ -101,7 +101,7 @@ export function astr(str: unknown) {
}

export function cloneDeep<T>(obj: T): T {
if (obj instanceof Uint8Array) {
if (isBytes(obj)) {
return Uint8Array.from(obj) as T;
} else if (Array.isArray(obj)) {
return obj.map(cloneDeep) as unknown as T;
Expand Down Expand Up @@ -148,7 +148,7 @@ export const formatters = {
//x = 0.01/price = 1/100 / price = 1/(100*price)
// float does not have enough precision
const totalPrice = fiatPrec.decode('' + price);
const centPrice = fiatPrec.decode('0.01') * 10n ** BigInt(precision);
const centPrice = fiatPrec.decode('0.01') * BigInt(10) ** BigInt(precision);
return centPrice / totalPrice;
},
// TODO: what difference between decimal and this?!
Expand Down Expand Up @@ -179,10 +179,10 @@ export const formatters = {

fromWei(wei: string | number | bigint) {
const GWEI = 10 ** 9;
const ETHER = 10n ** BigInt(ETH_PRECISION);
const ETHER = BigInt(10) ** BigInt(ETH_PRECISION);
wei = BigInt(wei);
if (wei < BigInt(GWEI) / 10n) return wei + 'wei';
if (wei >= BigInt(GWEI) && wei < ETHER / 1000n)
if (wei < BigInt(GWEI) / BigInt(10)) return wei + 'wei';
if (wei >= BigInt(GWEI) && wei < ETHER / BigInt(1000))
return formatters.formatBigint(wei, BigInt(GWEI), 9, false) + 'μeth';
return formatters.formatBigint(wei, ETHER, ETH_PRECISION, false) + 'eth';
},
Expand Down

0 comments on commit 3e481ca

Please sign in to comment.