Bytes library and CAIP2/CAIP10 helpers (#5252)
Co-authored-by: cairo <cairoeth@protonmail.com> Co-authored-by: Ernesto García <ernestognw@gmail.com> Co-authored-by: Arr00 <13561405+arr00@users.noreply.github.com>
This commit is contained in:
109
test/helpers/chains.js
Normal file
109
test/helpers/chains.js
Normal file
@ -0,0 +1,109 @@
|
||||
// NOTE: this file defines some examples of CAIP-2 and CAIP-10 identifiers.
|
||||
// The following listing does not pretend to be exhaustive or even accurate. It SHOULD NOT be used in production.
|
||||
|
||||
const { ethers } = require('hardhat');
|
||||
const { mapValues } = require('./iterate');
|
||||
|
||||
// EVM (https://axelarscan.io/resources/chains?type=evm)
|
||||
const ethereum = {
|
||||
Ethereum: '1',
|
||||
optimism: '10',
|
||||
binance: '56',
|
||||
Polygon: '137',
|
||||
Fantom: '250',
|
||||
fraxtal: '252',
|
||||
filecoin: '314',
|
||||
Moonbeam: '1284',
|
||||
centrifuge: '2031',
|
||||
kava: '2222',
|
||||
mantle: '5000',
|
||||
base: '8453',
|
||||
immutable: '13371',
|
||||
arbitrum: '42161',
|
||||
celo: '42220',
|
||||
Avalanche: '43114',
|
||||
linea: '59144',
|
||||
blast: '81457',
|
||||
scroll: '534352',
|
||||
aurora: '1313161554',
|
||||
};
|
||||
|
||||
// Cosmos (https://axelarscan.io/resources/chains?type=cosmos)
|
||||
const cosmos = {
|
||||
Axelarnet: 'axelar-dojo-1',
|
||||
osmosis: 'osmosis-1',
|
||||
cosmoshub: 'cosmoshub-4',
|
||||
juno: 'juno-1',
|
||||
'e-money': 'emoney-3',
|
||||
injective: 'injective-1',
|
||||
crescent: 'crescent-1',
|
||||
kujira: 'kaiyo-1',
|
||||
'secret-snip': 'secret-4',
|
||||
secret: 'secret-4',
|
||||
sei: 'pacific-1',
|
||||
stargaze: 'stargaze-1',
|
||||
assetmantle: 'mantle-1',
|
||||
fetch: 'fetchhub-4',
|
||||
ki: 'kichain-2',
|
||||
evmos: 'evmos_9001-2',
|
||||
aura: 'xstaxy-1',
|
||||
comdex: 'comdex-1',
|
||||
persistence: 'core-1',
|
||||
regen: 'regen-1',
|
||||
umee: 'umee-1',
|
||||
agoric: 'agoric-3',
|
||||
xpla: 'dimension_37-1',
|
||||
acre: 'acre_9052-1',
|
||||
stride: 'stride-1',
|
||||
carbon: 'carbon-1',
|
||||
sommelier: 'sommelier-3',
|
||||
neutron: 'neutron-1',
|
||||
rebus: 'reb_1111-1',
|
||||
archway: 'archway-1',
|
||||
provenance: 'pio-mainnet-1',
|
||||
ixo: 'ixo-5',
|
||||
migaloo: 'migaloo-1',
|
||||
teritori: 'teritori-1',
|
||||
haqq: 'haqq_11235-1',
|
||||
celestia: 'celestia',
|
||||
ojo: 'agamotto',
|
||||
chihuahua: 'chihuahua-1',
|
||||
saga: 'ssc-1',
|
||||
dymension: 'dymension_1100-1',
|
||||
fxcore: 'fxcore',
|
||||
c4e: 'perun-1',
|
||||
bitsong: 'bitsong-2b',
|
||||
nolus: 'pirin-1',
|
||||
lava: 'lava-mainnet-1',
|
||||
'terra-2': 'phoenix-1',
|
||||
terra: 'columbus-5',
|
||||
};
|
||||
|
||||
const makeCAIP = ({ namespace, reference, account }) => ({
|
||||
namespace,
|
||||
reference,
|
||||
account,
|
||||
caip2: `${namespace}:${reference}`,
|
||||
caip10: `${namespace}:${reference}:${account}`,
|
||||
toCaip10: other => `${namespace}:${reference}:${ethers.getAddress(other.target ?? other.address ?? other)}`,
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
CHAINS: mapValues(
|
||||
Object.assign(
|
||||
mapValues(ethereum, reference => ({
|
||||
namespace: 'eip155',
|
||||
reference,
|
||||
account: ethers.Wallet.createRandom().address,
|
||||
})),
|
||||
mapValues(cosmos, reference => ({
|
||||
namespace: 'cosmos',
|
||||
reference,
|
||||
account: ethers.encodeBase58(ethers.randomBytes(32)),
|
||||
})),
|
||||
),
|
||||
makeCAIP,
|
||||
),
|
||||
getLocalCAIP: account =>
|
||||
ethers.provider.getNetwork().then(({ chainId }) => makeCAIP({ namespace: 'eip155', reference: chainId, account })),
|
||||
};
|
||||
88
test/utils/Bytes.test.js
Normal file
88
test/utils/Bytes.test.js
Normal file
@ -0,0 +1,88 @@
|
||||
const { ethers } = require('hardhat');
|
||||
const { expect } = require('chai');
|
||||
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
|
||||
|
||||
async function fixture() {
|
||||
const mock = await ethers.deployContract('$Bytes');
|
||||
return { mock };
|
||||
}
|
||||
|
||||
const lorem = ethers.toUtf8Bytes(
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
|
||||
);
|
||||
const present = lorem.at(1);
|
||||
const absent = 255;
|
||||
|
||||
describe('Bytes', function () {
|
||||
before(async function () {
|
||||
Object.assign(this, await loadFixture(fixture));
|
||||
});
|
||||
|
||||
describe('indexOf', function () {
|
||||
it('first', async function () {
|
||||
expect(await this.mock.$indexOf(lorem, ethers.toBeHex(present))).to.equal(lorem.indexOf(present));
|
||||
});
|
||||
|
||||
it('from index', async function () {
|
||||
for (const start in Array(lorem.length + 10).fill()) {
|
||||
const index = lorem.indexOf(present, start);
|
||||
const result = index === -1 ? ethers.MaxUint256 : index;
|
||||
expect(await this.mock.$indexOf(lorem, ethers.toBeHex(present), ethers.Typed.uint256(start))).to.equal(result);
|
||||
}
|
||||
});
|
||||
|
||||
it('absent', async function () {
|
||||
expect(await this.mock.$indexOf(lorem, ethers.toBeHex(absent))).to.equal(ethers.MaxUint256);
|
||||
});
|
||||
});
|
||||
|
||||
describe('lastIndexOf', function () {
|
||||
it('first', async function () {
|
||||
expect(await this.mock.$lastIndexOf(lorem, ethers.toBeHex(present))).to.equal(lorem.lastIndexOf(present));
|
||||
});
|
||||
|
||||
it('from index', async function () {
|
||||
for (const start in Array(lorem.length + 10).fill()) {
|
||||
const index = lorem.lastIndexOf(present, start);
|
||||
const result = index === -1 ? ethers.MaxUint256 : index;
|
||||
expect(await this.mock.$lastIndexOf(lorem, ethers.toBeHex(present), ethers.Typed.uint256(start))).to.equal(
|
||||
result,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('absent', async function () {
|
||||
expect(await this.mock.$lastIndexOf(lorem, ethers.toBeHex(absent))).to.equal(ethers.MaxUint256);
|
||||
});
|
||||
});
|
||||
|
||||
describe('slice', function () {
|
||||
describe('slice(bytes, uint256)', function () {
|
||||
for (const [descr, start] of Object.entries({
|
||||
'start = 0': 0,
|
||||
'start within bound': 10,
|
||||
'start out of bound': 1000,
|
||||
})) {
|
||||
it(descr, async function () {
|
||||
const result = ethers.hexlify(lorem.slice(start));
|
||||
expect(await this.mock.$slice(lorem, start)).to.equal(result);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('slice(bytes, uint256, uint256)', function () {
|
||||
for (const [descr, [start, end]] of Object.entries({
|
||||
'start = 0': [0, 42],
|
||||
'start and end within bound': [17, 42],
|
||||
'end out of bound': [42, 1000],
|
||||
'start = end': [17, 17],
|
||||
'start > end': [42, 17],
|
||||
})) {
|
||||
it(descr, async function () {
|
||||
const result = ethers.hexlify(lorem.slice(start, end));
|
||||
expect(await this.mock.$slice(lorem, start, ethers.Typed.uint256(end))).to.equal(result);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
53
test/utils/CAIP.test.js
Normal file
53
test/utils/CAIP.test.js
Normal file
@ -0,0 +1,53 @@
|
||||
const { ethers } = require('hardhat');
|
||||
const { expect } = require('chai');
|
||||
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
|
||||
|
||||
const { CHAINS, getLocalCAIP } = require('../helpers/chains');
|
||||
|
||||
async function fixture() {
|
||||
const caip2 = await ethers.deployContract('$CAIP2');
|
||||
const caip10 = await ethers.deployContract('$CAIP10');
|
||||
return { caip2, caip10 };
|
||||
}
|
||||
|
||||
describe('CAIP utilities', function () {
|
||||
beforeEach(async function () {
|
||||
Object.assign(this, await loadFixture(fixture));
|
||||
});
|
||||
|
||||
describe('CAIP-2', function () {
|
||||
it('local()', async function () {
|
||||
const { caip2 } = await getLocalCAIP();
|
||||
expect(await this.caip2.$local()).to.equal(caip2);
|
||||
});
|
||||
|
||||
for (const { namespace, reference, caip2 } of Object.values(CHAINS))
|
||||
it(`format(${namespace}, ${reference})`, async function () {
|
||||
expect(await this.caip2.$format(namespace, reference)).to.equal(caip2);
|
||||
});
|
||||
|
||||
for (const { namespace, reference, caip2 } of Object.values(CHAINS))
|
||||
it(`parse(${caip2})`, async function () {
|
||||
expect(await this.caip2.$parse(caip2)).to.deep.equal([namespace, reference]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CAIP-10', function () {
|
||||
const { address: account } = ethers.Wallet.createRandom();
|
||||
|
||||
it(`local(${account})`, async function () {
|
||||
const { caip10 } = await getLocalCAIP(account);
|
||||
expect(await this.caip10.$local(ethers.Typed.address(account))).to.equal(caip10);
|
||||
});
|
||||
|
||||
for (const { account, caip2, caip10 } of Object.values(CHAINS))
|
||||
it(`format(${caip2}, ${account})`, async function () {
|
||||
expect(await this.caip10.$format(ethers.Typed.string(caip2), ethers.Typed.string(account))).to.equal(caip10);
|
||||
});
|
||||
|
||||
for (const { account, caip2, caip10 } of Object.values(CHAINS))
|
||||
it(`parse(${caip10})`, async function () {
|
||||
expect(await this.caip10.$parse(caip10)).to.deep.equal([caip2, account]);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user