@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
204 lines • 8.08 kB
JavaScript
import { expect } from 'chai';
import { ethers } from 'ethers';
import { ZBps } from '../metadata/customZodTypes.js';
import { CrossCollateralRoutingFeeConfigSchema, CrossCollateralRoutingFeeInputConfigSchema, LinearFeeInputConfigSchema, RoutingFeeInputConfigSchema, TokenFeeType, } from './types.js';
const SOME_ADDRESS = ethers.Wallet.createRandom().address;
describe('LinearFeeInputConfigSchema', () => {
it('should accept config with only bps', () => {
const config = {
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
bps: 100,
};
const result = LinearFeeInputConfigSchema.safeParse(config);
expect(result.success).to.be.true;
});
it('should accept config with only maxFee and halfAmount', () => {
const config = {
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
maxFee: 10000n,
halfAmount: 5000n,
};
const result = LinearFeeInputConfigSchema.safeParse(config);
expect(result.success).to.be.true;
});
it('should reject config with neither bps nor maxFee/halfAmount', () => {
const config = {
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
};
const result = LinearFeeInputConfigSchema.safeParse(config);
expect(result.success).to.be.false;
expect(result.error?.issues[0]?.message).to.include('Provide bps or both maxFee and halfAmount');
});
it('should reject config with only maxFee (missing halfAmount)', () => {
const config = {
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
maxFee: 10000n,
};
const result = LinearFeeInputConfigSchema.safeParse(config);
expect(result.success).to.be.false;
});
it('should reject config with only halfAmount (missing maxFee)', () => {
const config = {
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
halfAmount: 5000n,
};
const result = LinearFeeInputConfigSchema.safeParse(config);
expect(result.success).to.be.false;
});
it('should reject halfAmount = 0', () => {
const config = {
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
maxFee: 10000n,
halfAmount: 0n,
};
const result = LinearFeeInputConfigSchema.safeParse(config);
expect(result.success).to.be.false;
expect(result.error?.issues[0]?.message).to.include('halfAmount must be > 0');
});
it('should reject bps = 0', () => {
const config = {
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
bps: 0,
};
const result = LinearFeeInputConfigSchema.safeParse(config);
expect(result.success).to.be.false;
expect(result.error?.issues[0]?.message).to.include('bps must be > 0');
});
it('should accept config with both bps and maxFee/halfAmount and use explicit bps', () => {
const config = {
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
bps: 100,
maxFee: 10000n,
halfAmount: 5000n,
};
const result = LinearFeeInputConfigSchema.safeParse(config);
expect(result.success).to.be.true;
if (result.success) {
expect(result.data.bps).to.equal(100);
}
});
it('should compute bps from maxFee/halfAmount when only those are provided', () => {
const maxFee = 10000n;
const halfAmount = 5000n;
const config = {
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
maxFee,
halfAmount,
};
const result = LinearFeeInputConfigSchema.safeParse(config);
expect(result.success).to.be.true;
if (result.success) {
expect(result.data.bps).to.exist;
expect(result.data.bps).to.be.a('number');
}
});
});
describe('CrossCollateralRoutingFee schemas', () => {
it('rejects empty routing feeContracts for routing fee input config', () => {
const result = RoutingFeeInputConfigSchema.safeParse({
type: TokenFeeType.RoutingFee,
owner: SOME_ADDRESS,
feeContracts: {},
});
expect(result.success).to.equal(false);
});
it('rejects empty feeContracts for cross collateral input config', () => {
const result = CrossCollateralRoutingFeeInputConfigSchema.safeParse({
type: TokenFeeType.CrossCollateralRoutingFee,
owner: SOME_ADDRESS,
feeContracts: {},
});
expect(result.success).to.equal(false);
});
it('rejects empty destination entries for deployed config', () => {
const result = CrossCollateralRoutingFeeConfigSchema.safeParse({
type: TokenFeeType.CrossCollateralRoutingFee,
owner: SOME_ADDRESS,
feeContracts: {
ethereum: {},
},
});
expect(result.success).to.equal(false);
});
it('rejects empty destination entries for input config', () => {
const result = CrossCollateralRoutingFeeInputConfigSchema.safeParse({
type: TokenFeeType.CrossCollateralRoutingFee,
owner: SOME_ADDRESS,
feeContracts: {
ethereum: {},
},
});
expect(result.success).to.equal(false);
});
});
describe('LinearFeeInputConfigSchema — fractional bps', () => {
it('should accept fractional bps like 1.5', () => {
const result = LinearFeeInputConfigSchema.safeParse({
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
bps: 1.5,
});
expect(result.success).to.be.true;
if (result.success) {
expect(result.data.bps).to.equal(1.5);
}
});
it('should accept bps values that are IEEE 754 edge cases (e.g. 0.3)', () => {
const result = LinearFeeInputConfigSchema.safeParse({
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
bps: 0.3,
});
expect(result.success).to.be.true;
});
it('should reject bps with more than 4 decimal places', () => {
const result = LinearFeeInputConfigSchema.safeParse({
type: TokenFeeType.LinearFee,
owner: SOME_ADDRESS,
bps: 0.00001,
});
expect(result.success).to.be.false;
expect(result.error?.issues[0]?.message).to.include('at most 4 decimal places');
});
});
describe('ZBps schema validation', () => {
it('should reject invalid string inputs', () => {
expect(ZBps.safeParse('abc').success).to.be.false;
expect(ZBps.safeParse('').success).to.be.false;
expect(ZBps.safeParse('Infinity').success).to.be.false;
expect(ZBps.safeParse('NaN').success).to.be.false;
expect(ZBps.safeParse('-1').success).to.be.false;
});
it('should accept valid inputs and transform to number', () => {
const r1 = ZBps.safeParse('1.5');
expect(r1.success).to.be.true;
if (r1.success)
expect(r1.data).to.equal(1.5);
const r2 = ZBps.safeParse('5');
expect(r2.success).to.be.true;
if (r2.success)
expect(r2.data).to.equal(5);
const r3 = ZBps.safeParse(1.5);
expect(r3.success).to.be.true;
});
it('should reject bigint input (intentional breaking change — use plain number instead)', () => {
// ZBps intentionally dropped bigint support; callers must use number (e.g., 5 not 5n)
expect(ZBps.safeParse(5n).success).to.be.false;
expect(ZBps.safeParse(0n).success).to.be.false;
});
it('should reject negative numbers', () => {
expect(ZBps.safeParse(-5).success).to.be.false;
expect(ZBps.safeParse(-1.5).success).to.be.false;
expect(ZBps.safeParse(-0.0001).success).to.be.false;
});
});
//# sourceMappingURL=types.test.js.map