Move shared types and utilities to @abacus-network/utils (#194)

pull/206/head
Asa Oines 3 years ago committed by GitHub
parent a9709461b0
commit f022a770a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      package-lock.json
  2. 1
      package.json
  3. 5
      solidity/abacus-core/test/common.test.ts
  4. 77
      solidity/abacus-core/test/inbox.test.ts
  5. 2
      solidity/abacus-core/test/index.ts
  6. 2
      solidity/abacus-core/test/lib/AbacusDeployment.ts
  7. 61
      solidity/abacus-core/test/lib/core.ts
  8. 4
      solidity/abacus-core/test/lib/upgrade.ts
  9. 44
      solidity/abacus-core/test/lib/utils.ts
  10. 4
      solidity/abacus-core/test/merkle.test.ts
  11. 13
      solidity/abacus-core/test/message.test.ts
  12. 44
      solidity/abacus-core/test/outbox.test.ts
  13. 4
      solidity/abacus-core/test/upgrade.test.ts
  14. 14
      solidity/abacus-core/test/validatorManager.test.ts
  15. 4
      solidity/abacus-core/test/xAppConnectionManager.test.ts
  16. 4
      solidity/abacus-xapps/test/bridge/BridgeToken.test.ts
  17. 60
      solidity/abacus-xapps/test/bridge/EthHelper.test.ts
  18. 282
      solidity/abacus-xapps/test/bridge/bridge.test.ts
  19. 4
      solidity/abacus-xapps/test/bridge/bridgeMessage.test.ts
  20. 5
      solidity/abacus-xapps/test/bridge/lib/BridgeDeployment.ts
  21. 19
      solidity/abacus-xapps/test/governance/governanceRouter.test.ts
  22. 18
      solidity/abacus-xapps/test/governance/lib/GovernanceDeployment.ts
  23. 2
      solidity/abacus-xapps/test/governance/lib/utils.ts
  24. 1
      typescript/utils/.gitignore
  25. 2
      typescript/utils/index.ts
  26. 19
      typescript/utils/package.json
  27. 18
      typescript/utils/src/types.ts
  28. 75
      typescript/utils/src/utils.ts
  29. 12
      typescript/utils/tsconfig.json

20
package-lock.json generated

@ -6,6 +6,7 @@
"": {
"name": "@abacus-network/monorepo",
"workspaces": [
"typescript/utils",
"solidity/abacus-core",
"solidity/abacus-xapps",
"typescript/typechain",
@ -34,6 +35,10 @@
"resolved": "typescript/typechain",
"link": true
},
"node_modules/@abacus-network/utils": {
"resolved": "typescript/utils",
"link": true
},
"node_modules/@aws-crypto/ie11-detection": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz",
@ -26308,6 +26313,14 @@
"typescript/typechain/node_modules/@types/node": {
"version": "16.11.26",
"license": "MIT"
},
"typescript/utils": {
"version": "0.0.5",
"license": "MIT OR Apache-2.0",
"dependencies": {
"chai": "^4.3.0",
"ethers": "^5.4.7"
}
}
},
"dependencies": {
@ -26441,6 +26454,13 @@
}
}
},
"@abacus-network/utils": {
"version": "file:typescript/utils",
"requires": {
"chai": "^4.3.0",
"ethers": "^5.4.7"
}
},
"@aws-crypto/ie11-detection": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz",

@ -5,6 +5,7 @@
"prettier": "npm run prettier --workspaces"
},
"workspaces": [
"typescript/utils",
"solidity/abacus-core",
"solidity/abacus-xapps",
"typescript/typechain",

@ -1,15 +1,14 @@
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { ethers } from 'hardhat';
import { expect } from 'chai';
import { Address, Signer } from './lib/types';
import { TestCommon__factory, TestCommon } from '../typechain';
const localDomain = 1000;
const ONLY_OWNER_REVERT_MSG = 'Ownable: caller is not the owner';
describe('Common', async () => {
let owner: Signer, nonowner: Signer, common: TestCommon;
let owner: SignerWithAddress, nonowner: SignerWithAddress, common: TestCommon;
before(async () => {
[owner, nonowner] = await ethers.getSigners();

@ -1,14 +1,8 @@
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { ethers } from 'hardhat';
import { expect } from 'chai';
import {
formatMessage,
messageHash,
Validator,
AbacusState,
MessageStatus,
} from './lib/core';
import { Signer, BytesArray } from './lib/types';
import { types, utils } from '@abacus-network/utils';
import { Validator } from './lib/core';
import {
BadRecipient1__factory,
BadRecipient2__factory,
@ -45,9 +39,9 @@ describe('Inbox', async () => {
let inbox: TestInbox,
validatorManager: ValidatorManager,
signer: Signer,
fakeSigner: Signer,
abacusMessageSender: Signer,
signer: SignerWithAddress,
fakeSigner: SignerWithAddress,
abacusMessageSender: SignerWithAddress,
validator: Validator,
fakeValidator: Validator;
@ -126,11 +120,11 @@ describe('Inbox', async () => {
await inbox.setCheckpoint(testCase.expectedRoot, 1);
// Ensure proper static call return value
expect(await inbox.callStatic.prove(leaf, path as BytesArray, index)).to.be
.true;
expect(await inbox.callStatic.prove(leaf, path as types.BytesArray, index))
.to.be.true;
await inbox.prove(leaf, path as BytesArray, index);
expect(await inbox.messages(leaf)).to.equal(MessageStatus.PENDING);
await inbox.prove(leaf, path as types.BytesArray, index);
expect(await inbox.messages(leaf)).to.equal(types.MessageStatus.PENDING);
});
it('Rejects an already-proven message', async () => {
@ -140,12 +134,12 @@ describe('Inbox', async () => {
await inbox.setCheckpoint(testCase.expectedRoot, 1);
// Prove message, which changes status to MessageStatus.Pending
await inbox.prove(leaf, path as BytesArray, index);
expect(await inbox.messages(leaf)).to.equal(MessageStatus.PENDING);
await inbox.prove(leaf, path as types.BytesArray, index);
expect(await inbox.messages(leaf)).to.equal(types.MessageStatus.PENDING);
// Try to prove message again
await expect(
inbox.prove(leaf, path as BytesArray, index),
inbox.prove(leaf, path as types.BytesArray, index),
).to.be.revertedWith('!MessageStatus.None');
});
@ -163,11 +157,12 @@ describe('Inbox', async () => {
await inbox.setCheckpoint(testCase.expectedRoot, 1);
expect(await inbox.callStatic.prove(leaf, newPath as BytesArray, index)).to
.be.false;
expect(
await inbox.callStatic.prove(leaf, newPath as types.BytesArray, index),
).to.be.false;
await inbox.prove(leaf, newPath as BytesArray, index);
expect(await inbox.messages(leaf)).to.equal(MessageStatus.NONE);
await inbox.prove(leaf, newPath as types.BytesArray, index);
expect(await inbox.messages(leaf)).to.equal(types.MessageStatus.NONE);
});
it('Processes a proved message', async () => {
@ -177,7 +172,7 @@ describe('Inbox', async () => {
const testRecipient = await testRecipientFactory.deploy();
const nonce = 0;
const abacusMessage = formatMessage(
const abacusMessage = utils.formatMessage(
remoteDomain,
sender.address,
nonce,
@ -186,7 +181,7 @@ describe('Inbox', async () => {
'0x',
);
// Set message status to MessageStatus.Pending
// Set message status to types.MessageStatus.Pending
await inbox.setMessageProven(abacusMessage);
// Ensure proper static call return value
@ -196,7 +191,7 @@ describe('Inbox', async () => {
const processTx = inbox.process(abacusMessage);
await expect(processTx)
.to.emit(inbox, 'Process')
.withArgs(messageHash(abacusMessage), true, '0x');
.withArgs(utils.messageHash(abacusMessage), true, '0x');
});
it('Fails to process an unproved message', async () => {
@ -204,7 +199,7 @@ describe('Inbox', async () => {
const nonce = 0;
const body = ethers.utils.formatBytes32String('message');
const abacusMessage = formatMessage(
const abacusMessage = utils.formatMessage(
remoteDomain,
sender.address,
nonce,
@ -225,7 +220,7 @@ describe('Inbox', async () => {
const badRecipient = await factory.deploy();
const nonce = 0;
const abacusMessage = formatMessage(
const abacusMessage = utils.formatMessage(
remoteDomain,
sender.address,
nonce,
@ -245,7 +240,7 @@ describe('Inbox', async () => {
const nonce = 0;
const body = ethers.utils.formatBytes32String('message');
const abacusMessage = formatMessage(
const abacusMessage = utils.formatMessage(
remoteDomain,
sender.address,
nonce,
@ -264,7 +259,7 @@ describe('Inbox', async () => {
const nonce = 0;
const body = ethers.utils.formatBytes32String('message');
const abacusMessage = formatMessage(
const abacusMessage = utils.formatMessage(
remoteDomain,
abacusMessageSender.address,
nonce,
@ -273,7 +268,7 @@ describe('Inbox', async () => {
body,
);
// Set message status to MessageStatus.Pending
// Set message status to types.MessageStatus.Pending
await inbox.setMessageProven(abacusMessage);
await expect(inbox.process(abacusMessage)).to.not.be.reverted;
});
@ -283,7 +278,7 @@ describe('Inbox', async () => {
const nonce = 0;
const body = ethers.utils.formatBytes32String('message');
const abacusMessage = formatMessage(
const abacusMessage = utils.formatMessage(
remoteDomain,
sender.address,
nonce,
@ -308,7 +303,7 @@ describe('Inbox', async () => {
const testRecipient = await factory.deploy();
const nonce = 0;
const abacusMessage = formatMessage(
const abacusMessage = utils.formatMessage(
remoteDomain,
sender.address,
nonce,
@ -334,7 +329,7 @@ describe('Inbox', async () => {
// Note that hash of this message specifically matches leaf of 1st
// proveAndProcess test case
const abacusMessage = formatMessage(
const abacusMessage = utils.formatMessage(
remoteDomain,
sender.address,
nonce,
@ -345,7 +340,7 @@ describe('Inbox', async () => {
// Assert above message and test case have matching leaves
const { path, index } = proveAndProcessTestCases[0];
const hash = messageHash(abacusMessage);
const hash = utils.messageHash(abacusMessage);
// Set inbox's current root to match newly computed root that includes
// the new leaf (normally root will have already been computed and path
@ -354,14 +349,14 @@ describe('Inbox', async () => {
// simply recalculate root with the leaf using branchRoot)
const proofRoot = await inbox.testBranchRoot(
hash,
path as BytesArray,
path as types.BytesArray,
index,
);
await inbox.setCheckpoint(proofRoot, 1);
await inbox.proveAndProcess(abacusMessage, path as BytesArray, index);
await inbox.proveAndProcess(abacusMessage, path as types.BytesArray, index);
expect(await inbox.messages(hash)).to.equal(MessageStatus.PROCESSED);
expect(await inbox.messages(hash)).to.equal(types.MessageStatus.PROCESSED);
});
it('Has proveAndProcess fail if prove fails', async () => {
@ -373,7 +368,7 @@ describe('Inbox', async () => {
let { leaf, index, path } = testCase.proofs[0];
// Create arbitrary message (contents not important)
const abacusMessage = formatMessage(
const abacusMessage = utils.formatMessage(
remoteDomain,
sender.address,
nonce,
@ -386,14 +381,14 @@ describe('Inbox', async () => {
// inbox.prove(...) will fail
const proofRoot = await inbox.testBranchRoot(
leaf,
path as BytesArray,
path as types.BytesArray,
index,
);
const rootIndex = await inbox.checkpoints(proofRoot);
expect(rootIndex).to.equal(0);
await expect(
inbox.proveAndProcess(abacusMessage, path as BytesArray, index),
inbox.proveAndProcess(abacusMessage, path as types.BytesArray, index),
).to.be.revertedWith('!prove');
});
});

@ -1,4 +1,2 @@
export * as types from './lib/types';
export * as utils from './lib/utils';
export * as core from './lib/core';
export { AbacusDeployment } from './lib/AbacusDeployment';

@ -1,8 +1,8 @@
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { assert } from 'chai';
import * as ethers from 'ethers';
import { types } from '@abacus-network/utils';
import * as types from './types';
import { Validator } from './core';
import {

@ -1,8 +1,5 @@
import { assert } from 'chai';
import * as ethers from 'ethers';
import { addressToBytes32 } from './utils';
import * as types from './types';
import { ethers } from 'ethers';
import { utils, types } from '@abacus-network/utils';
export class Validator {
localDomain: types.Domain;
@ -28,7 +25,7 @@ export class Validator {
}
domainHash() {
return domainHash(this.localDomain);
return utils.domainHash(this.localDomain);
}
message(root: types.HexString, index: number) {
@ -50,55 +47,3 @@ export class Validator {
};
}
}
export const formatMessage = (
localDomain: types.Domain,
senderAddr: types.Address,
sequence: number,
destinationDomain: types.Domain,
recipientAddr: types.Address,
body: types.HexString,
): string => {
senderAddr = addressToBytes32(senderAddr);
recipientAddr = addressToBytes32(recipientAddr);
return ethers.utils.solidityPack(
['uint32', 'bytes32', 'uint32', 'uint32', 'bytes32', 'bytes'],
[localDomain, senderAddr, sequence, destinationDomain, recipientAddr, body],
);
};
export enum AbacusState {
UNINITIALIZED = 0,
ACTIVE,
FAILED,
}
export enum MessageStatus {
NONE = 0,
PENDING,
PROCESSED,
}
export function messageHash(message: types.HexString): string {
return ethers.utils.solidityKeccak256(['bytes'], [message]);
}
export function destinationAndNonce(
destination: types.Domain,
sequence: number,
): ethers.BigNumber {
assert(destination < Math.pow(2, 32) - 1);
assert(sequence < Math.pow(2, 32) - 1);
return ethers.BigNumber.from(destination)
.mul(ethers.BigNumber.from(2).pow(32))
.add(ethers.BigNumber.from(sequence));
}
export function domainHash(domain: Number): string {
return ethers.utils.solidityKeccak256(
['uint32', 'string'],
[domain, 'OPTICS'],
);
}

@ -1,7 +1,7 @@
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { expect } from 'chai';
import ethers from 'ethers';
import { Signer } from './types';
import {
MysteryMathV1,
MysteryMathV2,
@ -24,7 +24,7 @@ export class UpgradeTestHelpers {
stateVar: number = 17;
async deployMysteryMathUpgradeSetup(
signer: Signer,
signer: SignerWithAddress,
ubc: UpgradeBeaconController,
): Promise<MysteryMathUpgrade> {
// deploy implementation

@ -1,44 +0,0 @@
import { ethers } from 'ethers';
import { Address } from './types';
/*
* Gets the byte length of a hex string
*
* @param hexStr - the hex string
* @return byteLength - length in bytes
*/
export function getHexStringByteLength(hexStr: string) {
let len = hexStr.length;
// check for prefix, remove if necessary
if (hexStr.slice(0, 2) == '0x') {
len -= 2;
}
// divide by 2 to get the byte length
return len / 2;
}
/*
* Converts address to Bytes32
*
* @param address - the address
* @return The address as bytes32
*/
export function toBytes32(address: Address): string {
return '0x' + '00'.repeat(12) + address.slice(2);
}
export const stringToBytes32 = (s: string): string => {
const str = Buffer.from(s.slice(0, 32), 'utf-8');
const result = Buffer.alloc(32);
str.copy(result);
return '0x' + result.toString('hex');
};
export function addressToBytes32(address: Address): string {
return ethers.utils
.hexZeroPad(ethers.utils.hexStripZeros(address), 32)
.toLowerCase();
}

@ -1,7 +1,7 @@
import { ethers } from 'hardhat';
import { expect } from 'chai';
import { types } from '@abacus-network/utils';
import { BytesArray } from './lib/types';
import { TestMerkle, TestMerkle__factory } from '../typechain';
const merkleTestCases = require('../../../vectors/merkle.json');
@ -41,7 +41,7 @@ describe('Merkle', async () => {
const proofRoot = await merkle.branchRoot(
leaf,
path as BytesArray,
path as types.BytesArray,
index,
);
expect(proofRoot).to.equal(expectedRoot);

@ -1,7 +1,6 @@
import { ethers } from 'hardhat';
import { expect } from 'chai';
import { formatMessage, messageHash as msgHash } from './lib/core';
import { addressToBytes32 } from './lib/utils';
import { utils } from '@abacus-network/utils';
import { TestMessage, TestMessage__factory } from '../typechain';
const testCases = require('../../../vectors/message.json');
@ -24,7 +23,7 @@ describe('Message', async () => {
const nonce = 1;
const body = ethers.utils.formatBytes32String('message');
const message = formatMessage(
const message = utils.formatMessage(
remoteDomain,
sender.address,
nonce,
@ -35,12 +34,12 @@ describe('Message', async () => {
expect(await messageLib.origin(message)).to.equal(remoteDomain);
expect(await messageLib.sender(message)).to.equal(
addressToBytes32(sender.address),
utils.addressToBytes32(sender.address),
);
expect(await messageLib.nonce(message)).to.equal(nonce);
expect(await messageLib.destination(message)).to.equal(localDomain);
expect(await messageLib.recipient(message)).to.equal(
addressToBytes32(recipient.address),
utils.addressToBytes32(recipient.address),
);
expect(await messageLib.recipientAddress(message)).to.equal(
recipient.address,
@ -56,7 +55,7 @@ describe('Message', async () => {
const recipient = '0x2222222222222222222222222222222222222222';
const body = '0x1234';
const abacusMessage = formatMessage(
const abacusMessage = utils.formatMessage(
origin,
sender,
nonce,
@ -86,6 +85,6 @@ describe('Message', async () => {
ethers.utils.hexlify(testBody),
);
expect(await messageLib.leaf(abacusMessage)).to.equal(messageHash);
expect(msgHash(abacusMessage)).to.equal(messageHash);
expect(utils.messageHash(abacusMessage)).to.equal(messageHash);
});
});

@ -1,14 +1,8 @@
import { ethers } from 'hardhat';
import { expect } from 'chai';
import { Signer } from './lib/types';
import {
AbacusState,
Validator,
formatMessage,
messageHash,
destinationAndNonce,
} from './lib/core';
import { addressToBytes32 } from './lib/utils';
import { types, utils } from '@abacus-network/utils';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { Validator } from './lib/core';
import { TestOutbox, TestOutbox__factory } from '../typechain';
@ -18,7 +12,9 @@ const localDomain = 1000;
const destDomain = 2000;
describe('Outbox', async () => {
let outbox: TestOutbox, signer: Signer, recipient: Signer;
let outbox: TestOutbox,
signer: SignerWithAddress,
recipient: SignerWithAddress;
before(async () => {
[signer, recipient] = await ethers.getSigners();
@ -42,11 +38,15 @@ describe('Outbox', async () => {
it('ValidatorManager can fail', async () => {
await outbox.testSetValidatorManager(signer.address);
await outbox.fail();
expect(await outbox.state()).to.equal(AbacusState.FAILED);
expect(await outbox.state()).to.equal(types.AbacusState.FAILED);
const message = ethers.utils.formatBytes32String('message');
await expect(
outbox.dispatch(destDomain, addressToBytes32(recipient.address), message),
outbox.dispatch(
destDomain,
utils.addressToBytes32(recipient.address),
message,
),
).to.be.revertedWith('failed state');
});
@ -59,7 +59,11 @@ describe('Outbox', async () => {
it('Does not dispatch too large messages', async () => {
const message = `0x${Buffer.alloc(3000).toString('hex')}`;
await expect(
outbox.dispatch(destDomain, addressToBytes32(recipient.address), message),
outbox.dispatch(
destDomain,
utils.addressToBytes32(recipient.address),
message,
),
).to.be.revertedWith('msg too long');
});
@ -68,9 +72,9 @@ describe('Outbox', async () => {
const nonce = await outbox.nonces(localDomain);
// Format data that will be emitted from Dispatch event
const destAndNonce = destinationAndNonce(destDomain, nonce);
const destAndNonce = utils.destinationAndNonce(destDomain, nonce);
const abacusMessage = formatMessage(
const abacusMessage = utils.formatMessage(
localDomain,
signer.address,
nonce,
@ -78,7 +82,7 @@ describe('Outbox', async () => {
recipient.address,
message,
);
const hash = messageHash(abacusMessage);
const hash = utils.messageHash(abacusMessage);
const leafIndex = await outbox.tree();
const [checkpointedRoot] = await outbox.latestCheckpoint();
@ -86,7 +90,11 @@ describe('Outbox', async () => {
await expect(
outbox
.connect(signer)
.dispatch(destDomain, addressToBytes32(recipient.address), message),
.dispatch(
destDomain,
utils.addressToBytes32(recipient.address),
message,
),
)
.to.emit(outbox, 'Dispatch')
.withArgs(hash, leafIndex, destAndNonce, checkpointedRoot, abacusMessage);
@ -96,7 +104,7 @@ describe('Outbox', async () => {
const message = ethers.utils.formatBytes32String('message');
await outbox.dispatch(
destDomain,
addressToBytes32(recipient.address),
utils.addressToBytes32(recipient.address),
message,
);
await outbox.checkpoint();

@ -1,7 +1,7 @@
import { ethers } from 'hardhat';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { UpgradeTestHelpers, MysteryMathUpgrade } from './lib/upgrade';
import { Signer } from './lib/types';
import {
UpgradeBeaconController__factory,
UpgradeBeaconController,
@ -10,7 +10,7 @@ import {
describe('Upgrade', async () => {
const utils = new UpgradeTestHelpers();
let signer: Signer,
let signer: SignerWithAddress,
mysteryMath: MysteryMathUpgrade,
ubc: UpgradeBeaconController;

@ -1,9 +1,9 @@
import { ethers } from 'hardhat';
import { expect } from 'chai';
import { types, utils } from '@abacus-network/utils';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { AbacusState, Validator } from './lib/core';
import { addressToBytes32 } from './lib/utils';
import { Signer } from './lib/types';
import { Validator } from './lib/core';
import {
Outbox__factory,
@ -16,8 +16,8 @@ const outboxDomainHashCases = require('../../../vectors/outboxDomainHash.json');
const localDomain = 1000;
describe('ValidatorManager', async () => {
let signer: Signer,
fakeSigner: Signer,
let signer: SignerWithAddress,
fakeSigner: SignerWithAddress,
validatorManager: ValidatorManager,
validator: Validator,
fakeValidator: Validator;
@ -105,7 +105,7 @@ describe('ValidatorManager', async () => {
index,
signature,
);
expect(await outbox.state()).to.equal(AbacusState.FAILED);
expect(await outbox.state()).to.equal(types.AbacusState.FAILED);
});
it('Rejects improper checkpoint from non-validator', async () => {
@ -128,7 +128,7 @@ describe('ValidatorManager', async () => {
const message = `0x${Buffer.alloc(10).toString('hex')}`;
await outbox.dispatch(
localDomain,
addressToBytes32(signer.address),
utils.addressToBytes32(signer.address),
message,
);
await outbox.checkpoint();

@ -1,5 +1,6 @@
import { ethers } from 'hardhat';
import { expect } from 'chai';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import {
TestOutbox__factory,
@ -9,7 +10,6 @@ import {
TestInbox,
} from '../typechain';
import { Validator } from './lib/core';
import { Signer } from './lib/types';
const signedFailureTestCases = require('../../../vectors/signedFailure.json');
@ -22,7 +22,7 @@ const reserveGas = 15000;
describe('XAppConnectionManager', async () => {
let connectionManager: XAppConnectionManager,
enrolledInbox: TestInbox,
signer: Signer,
signer: SignerWithAddress,
validator: Validator;
before(async () => {

@ -1,15 +1,15 @@
import { ethers } from 'hardhat';
import { expect } from 'chai';
import { Wallet } from 'ethers';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { Signer } from '@abacus-network/abacus-sol/test/lib/types';
import { permitDigest } from './lib/permit';
import { BridgeToken__factory, BridgeToken } from '../../typechain';
const VALUE = 100;
describe('BridgeToken', async () => {
let deployer: Signer, permitee: Signer;
let deployer: SignerWithAddress, permitee: SignerWithAddress;
let token: BridgeToken;
let user: Wallet;
before(async () => {

@ -1,26 +1,26 @@
import { ethers } from 'hardhat';
import { BytesLike, Signer } from 'ethers';
import { BytesLike } from 'ethers';
import { expect } from 'chai';
import { utils } from '@abacus-network/utils';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import * as types from './lib/types';
import { serializeMessage } from './lib/utils';
import { BridgeDeployment } from './lib/BridgeDeployment';
import { AbacusDeployment, utils } from '@abacus-network/abacus-sol/test';
import { AbacusDeployment } from '@abacus-network/abacus-sol/test';
const localDomain = 1000;
const remoteDomain = 2000;
const domains = [localDomain, remoteDomain];
describe('EthHelper', async () => {
let abacusDeployment: AbacusDeployment;
let bridgeDeployment: BridgeDeployment;
let abacus: AbacusDeployment;
let bridge: BridgeDeployment;
let deployer: Signer;
let deployerAddress: string;
let deployer: SignerWithAddress;
let deployerId: string;
let recipient: Signer;
let recipientAddress: string;
let recipient: SignerWithAddress;
let recipientId: string;
let transferToSelfMessage: BytesLike;
@ -30,19 +30,14 @@ describe('EthHelper', async () => {
before(async () => {
[deployer, recipient] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
deployerId = utils.toBytes32(deployerAddress).toLowerCase();
recipientAddress = await recipient.getAddress();
recipientId = utils.toBytes32(recipientAddress).toLowerCase();
abacusDeployment = await AbacusDeployment.fromDomains(domains, deployer);
bridgeDeployment = await BridgeDeployment.fromAbacusDeployment(
abacusDeployment,
deployer,
);
deployerId = utils.addressToBytes32(deployer.address);
recipientId = utils.addressToBytes32(recipient.address);
abacus = await AbacusDeployment.fromDomains(domains, deployer);
bridge = await BridgeDeployment.fromAbacusDeployment(abacus, deployer);
const tokenId: types.TokenIdentifier = {
domain: localDomain,
id: utils.toBytes32(bridgeDeployment.weth(localDomain).address),
id: utils.addressToBytes32(bridge.weth(localDomain).address),
};
const transferToSelfMessageObj: types.Message = {
tokenId,
@ -66,39 +61,28 @@ describe('EthHelper', async () => {
});
it('send function', async () => {
let sendTx = bridgeDeployment.helper(localDomain).send(remoteDomain, {
let sendTx = bridge.helper(localDomain).send(remoteDomain, {
value,
});
await expect(sendTx).to.emit(
abacusDeployment.outbox(localDomain),
'Dispatch',
);
await expect(sendTx).to.emit(abacus.outbox(localDomain), 'Dispatch');
});
it('sendTo function', async () => {
let sendTx = bridgeDeployment
.helper(localDomain)
.sendTo(remoteDomain, recipientId, {
value,
});
let sendTx = bridge.helper(localDomain).sendTo(remoteDomain, recipientId, {
value,
});
await expect(sendTx).to.emit(
abacusDeployment.outbox(localDomain),
'Dispatch',
);
await expect(sendTx).to.emit(abacus.outbox(localDomain), 'Dispatch');
});
it('sendToEVMLike function', async () => {
let sendTx = bridgeDeployment
let sendTx = bridge
.helper(localDomain)
.sendToEVMLike(remoteDomain, recipientAddress, {
.sendToEVMLike(remoteDomain, recipient.address, {
value,
});
await expect(sendTx).to.emit(
abacusDeployment.outbox(localDomain),
'Dispatch',
);
await expect(sendTx).to.emit(abacus.outbox(localDomain), 'Dispatch');
});
});

@ -1,13 +1,11 @@
import { expect } from 'chai';
import { ethers } from 'hardhat';
import { BigNumber, BytesLike } from 'ethers';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import {
utils,
types as core,
AbacusDeployment,
} from '@abacus-network/abacus-sol/test';
import { AbacusDeployment } from '@abacus-network/abacus-sol/test';
import * as types from './lib/types';
import { utils } from '@abacus-network/utils';
import { serializeMessage } from './lib/utils';
import { BridgeDeployment } from './lib/BridgeDeployment';
import { BridgeToken, BridgeToken__factory, IERC20 } from '../../typechain';
@ -22,10 +20,9 @@ const testTokenId = {
};
describe('BridgeRouter', async () => {
let abacusDeployment: AbacusDeployment;
let bridgeDeployment: BridgeDeployment;
let deployer: core.Signer;
let deployerAddress: string;
let abacus: AbacusDeployment;
let bridge: BridgeDeployment;
let deployer: SignerWithAddress;
let deployerId: BytesLike;
const PROTOCOL_PROCESS_GAS = 800_000;
@ -36,31 +33,27 @@ describe('BridgeRouter', async () => {
before(async () => {
// populate deployer signer
[deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
deployerId = utils.toBytes32(await deployer.getAddress()).toLowerCase();
abacusDeployment = await AbacusDeployment.fromDomains(domains, deployer);
deployerId = utils.addressToBytes32(deployer.address);
abacus = await AbacusDeployment.fromDomains(domains, deployer);
// Enroll ourselves as a inbox so we can send messages directly to the
// local router.
await abacusDeployment
await abacus
.connectionManager(localDomain)
.enrollInbox(deployerAddress, remoteDomain);
.enrollInbox(deployer.address, remoteDomain);
});
beforeEach(async () => {
bridgeDeployment = await BridgeDeployment.fromAbacusDeployment(
abacusDeployment,
deployer,
);
bridge = await BridgeDeployment.fromAbacusDeployment(abacus, deployer);
// Enroll ourselves as a router so we can send messages directly to the
// local router.
await bridgeDeployment
await bridge
.router(localDomain)
.enrollRemoteRouter(remoteDomain, deployerId);
});
describe('invalid messages', async () => {
it('rejects invalid messages', async () => {
const handleTx = bridgeDeployment
const handleTx = bridge
.router(localDomain)
.handle(remoteDomain, deployerId, '0x', {
gasLimit: PROTOCOL_PROCESS_GAS,
@ -89,27 +82,20 @@ describe('BridgeRouter', async () => {
// Send a message to the local BridgeRouter triggering a BridgeToken
// deployment.
handleTx = await bridgeDeployment
handleTx = await bridge
.router(localDomain)
.handle(remoteDomain, deployerId, transferMessage, {
gasLimit: PROTOCOL_PROCESS_GAS,
});
repr = await bridgeDeployment.bridgeToken(
localDomain,
remoteDomain,
testToken,
);
repr = await bridge.bridgeToken(localDomain, remoteDomain, testToken);
});
it('deploys a token on first inbound transfer', async () => {
await expect(handleTx).to.emit(
bridgeDeployment.router(localDomain),
bridge.router(localDomain),
'TokenDeployed',
);
await expect(handleTx).to.emit(
abacusDeployment.outbox(localDomain),
'Dispatch',
);
await expect(handleTx).to.emit(abacus.outbox(localDomain), 'Dispatch');
expect(await repr.balanceOf(deployer.address)).to.equal(
BigNumber.from(TOKEN_VALUE),
);
@ -117,7 +103,7 @@ describe('BridgeRouter', async () => {
});
it('errors on send if ERC20 balance is insufficient', async () => {
const stealTx = bridgeDeployment
const stealTx = bridge
.router(localDomain)
.send(repr.address, TOKEN_VALUE * 10, remoteDomain, deployerId);
@ -128,7 +114,7 @@ describe('BridgeRouter', async () => {
it('errors when missing a remote router', async () => {
await expect(
bridgeDeployment
bridge
.router(localDomain)
.send(repr.address, TOKEN_VALUE, 121234, deployerId),
).to.be.revertedWith('!router');
@ -136,7 +122,7 @@ describe('BridgeRouter', async () => {
it('errors on send when recipient is the 0 address', async () => {
await expect(
bridgeDeployment
bridge
.router(localDomain)
.send(
repr.address,
@ -148,7 +134,7 @@ describe('BridgeRouter', async () => {
});
it('errors on send if ERC20 amount is zero', async () => {
const zeroTx = bridgeDeployment
const zeroTx = bridge
.router(localDomain)
.send(repr.address, 0, remoteDomain, deployerId);
@ -156,7 +142,7 @@ describe('BridgeRouter', async () => {
});
it('errors on send if remote router is unknown', async () => {
const unknownRemote = bridgeDeployment
const unknownRemote = bridge
.router(localDomain)
.send(repr.address, 1, 3000, deployerId);
@ -165,21 +151,18 @@ describe('BridgeRouter', async () => {
it('burns tokens on outbound message', async () => {
// OUTBOUND
const sendTx = bridgeDeployment
const sendTx = bridge
.router(localDomain)
.send(repr.address, TOKEN_VALUE, remoteDomain, deployerId);
await expect(sendTx).to.emit(
abacusDeployment.outbox(localDomain),
'Dispatch',
);
await expect(sendTx).to.emit(abacus.outbox(localDomain), 'Dispatch');
expect(await repr.totalSupply()).to.equal(BigNumber.from(0));
});
it('errors on outbound messages with not enough balance', async () => {
// OUTBOUND, NOT ENOUGH Tokens
const badTx = bridgeDeployment
const badTx = bridge
.router(localDomain)
.send(repr.address, TOKEN_VALUE + 1, remoteDomain, deployerId);
await expect(badTx).to.be.revertedWith(
@ -195,13 +178,13 @@ describe('BridgeRouter', async () => {
beforeEach(async () => {
localToken = await new BridgeToken__factory(deployer).deploy();
await localToken.initialize();
await localToken.mint(deployerAddress, TOKEN_VALUE);
await localToken.mint(deployer.address, TOKEN_VALUE);
// generate protocol messages
const transferMessageObj: types.Message = {
tokenId: {
domain: localDomain,
id: utils.toBytes32(localToken.address),
id: utils.addressToBytes32(localToken.address),
},
action: {
type: types.BridgeMessageTypes.TRANSFER,
@ -211,19 +194,17 @@ describe('BridgeRouter', async () => {
};
transferMessage = serializeMessage(transferMessageObj);
expect(await localToken.balanceOf(deployerAddress)).to.equal(
expect(await localToken.balanceOf(deployer.address)).to.equal(
BigNumber.from(TOKEN_VALUE),
);
expect(
await localToken.balanceOf(
bridgeDeployment.router(localDomain).address,
),
await localToken.balanceOf(bridge.router(localDomain).address),
).to.equal(BigNumber.from(0));
});
it('errors if the token is not approved', async () => {
// TOKEN NOT APPROVED
const unapproved = bridgeDeployment
const unapproved = bridge
.router(localDomain)
.send(localToken.address, 1, remoteDomain, deployerId);
@ -231,19 +212,17 @@ describe('BridgeRouter', async () => {
'ERC20: transfer amount exceeds allowance',
);
expect(
await localToken.balanceOf(
bridgeDeployment.router(localDomain).address,
),
await localToken.balanceOf(bridge.router(localDomain).address),
).to.equal(BigNumber.from(0));
});
it('errors if insufficient balance', async () => {
await localToken.approve(
bridgeDeployment.router(localDomain).address,
bridge.router(localDomain).address,
ethers.constants.MaxUint256,
);
const badTx = bridgeDeployment
const badTx = bridge
.router(localDomain)
.send(localToken.address, TOKEN_VALUE + 1, remoteDomain, deployerId);
@ -251,62 +230,53 @@ describe('BridgeRouter', async () => {
'ERC20: transfer amount exceeds balance',
);
expect(
await localToken.balanceOf(
bridgeDeployment.router(localDomain).address,
),
await localToken.balanceOf(bridge.router(localDomain).address),
).to.equal(BigNumber.from(0));
});
it('holds tokens on outbound transfer', async () => {
await localToken.approve(
bridgeDeployment.router(localDomain).address,
bridge.router(localDomain).address,
ethers.constants.MaxUint256,
);
const sendTx = await bridgeDeployment
const sendTx = await bridge
.router(localDomain)
.send(localToken.address, TOKEN_VALUE, remoteDomain, deployerId);
await expect(sendTx).to.emit(
abacusDeployment.outbox(localDomain),
'Dispatch',
);
await expect(sendTx).to.emit(abacus.outbox(localDomain), 'Dispatch');
expect(
await localToken.balanceOf(
bridgeDeployment.router(localDomain).address,
),
await localToken.balanceOf(bridge.router(localDomain).address),
).to.equal(BigNumber.from(TOKEN_VALUE));
});
it('unlocks tokens on inbound transfer', async () => {
await localToken.approve(
bridgeDeployment.router(localDomain).address,
bridge.router(localDomain).address,
ethers.constants.MaxUint256,
);
const sendTx = await bridgeDeployment
const sendTx = await bridge
.router(localDomain)
.send(localToken.address, TOKEN_VALUE, remoteDomain, deployerId);
let handleTx = await bridgeDeployment
let handleTx = await bridge
.router(localDomain)
.handle(remoteDomain, deployerId, transferMessage, {
gasLimit: PROTOCOL_PROCESS_GAS,
});
expect(handleTx).to.not.emit(
bridgeDeployment.router(localDomain),
bridge.router(localDomain),
'TokenDeployed',
);
expect(
await localToken.balanceOf(
bridgeDeployment.router(localDomain).address,
),
await localToken.balanceOf(bridge.router(localDomain).address),
).to.equal(BigNumber.from(0));
expect(await localToken.balanceOf(deployerAddress)).to.equal(
expect(await localToken.balanceOf(deployer.address)).to.equal(
BigNumber.from(TOKEN_VALUE),
);
});
@ -327,7 +297,7 @@ describe('BridgeRouter', async () => {
const transferMessage = serializeMessage(transferMessageObj);
expect(
bridgeDeployment.router(localDomain).preFill(transferMessage),
bridge.router(localDomain).preFill(transferMessage),
).to.be.revertedWith('!token');
});
@ -341,7 +311,7 @@ describe('BridgeRouter', async () => {
beforeEach(async () => {
// generate actions
recipient = `0x${'00'.repeat(19)}ff`;
recipientId = utils.toBytes32(recipient);
recipientId = utils.addressToBytes32(recipient);
// transfer message
const transferMessageObj: types.Message = {
@ -366,55 +336,55 @@ describe('BridgeRouter', async () => {
setupMessage = serializeMessage(setupMessageObj);
// perform setup
const setupTx = await bridgeDeployment
const setupTx = await bridge
.router(localDomain)
.handle(remoteDomain, deployerId, setupMessage, {
gasLimit: PROTOCOL_PROCESS_GAS,
});
await expect(setupTx).to.emit(
bridgeDeployment.router(localDomain),
bridge.router(localDomain),
'TokenDeployed',
);
repr = await bridgeDeployment.bridgeToken(
localDomain,
remoteDomain,
testToken,
);
repr = await bridge.bridgeToken(localDomain, remoteDomain, testToken);
expect(await repr.balanceOf(deployerAddress)).to.equal(
expect(await repr.balanceOf(deployer.address)).to.equal(
BigNumber.from(TOKEN_VALUE),
);
await repr.approve(
bridgeDeployment.router(localDomain).address,
bridge.router(localDomain).address,
ethers.constants.MaxUint256,
);
});
it('transfers tokens on a prefill', async () => {
const prefillTx = await bridgeDeployment
const prefillTx = await bridge
.router(localDomain)
.preFill(transferMessage);
await expect(prefillTx)
.to.emit(repr, 'Transfer')
.withArgs(
deployerAddress,
deployer.address,
recipient,
BigNumber.from(TOKEN_VALUE).mul(9995).div(10000),
);
});
it('mints tokens for the liquidity provider on message receipt', async () => {
await bridgeDeployment.router(localDomain).preFill(transferMessage);
let deliver = bridgeDeployment
await bridge.router(localDomain).preFill(transferMessage);
let deliver = bridge
.router(localDomain)
.handle(remoteDomain, deployerId, transferMessage, {
gasLimit: PROTOCOL_PROCESS_GAS,
});
await expect(deliver)
.to.emit(repr, 'Transfer')
.withArgs(ethers.constants.AddressZero, deployerAddress, TOKEN_VALUE);
.withArgs(
ethers.constants.AddressZero,
deployer.address,
TOKEN_VALUE,
);
});
});
@ -427,33 +397,28 @@ describe('BridgeRouter', async () => {
beforeEach(async () => {
localToken = await new BridgeToken__factory(deployer).deploy();
await localToken.initialize();
await localToken.mint(deployerAddress, TOKEN_VALUE);
await localToken.mint(
bridgeDeployment.router(localDomain).address,
TOKEN_VALUE,
);
await localToken.mint(deployer.address, TOKEN_VALUE);
await localToken.mint(bridge.router(localDomain).address, TOKEN_VALUE);
await localToken.approve(
bridgeDeployment.router(localDomain).address,
bridge.router(localDomain).address,
ethers.constants.MaxUint256,
);
expect(await localToken.balanceOf(deployerAddress)).to.equal(
expect(await localToken.balanceOf(deployer.address)).to.equal(
BigNumber.from(TOKEN_VALUE),
);
expect(
await localToken.balanceOf(
bridgeDeployment.router(localDomain).address,
),
await localToken.balanceOf(bridge.router(localDomain).address),
).to.equal(BigNumber.from(TOKEN_VALUE));
// generate transfer action
recipient = `0x${'00'.repeat(19)}ff`;
recipientId = utils.toBytes32(recipient);
recipientId = utils.addressToBytes32(recipient);
const transferMessageObj: types.Message = {
tokenId: {
domain: localDomain,
id: utils.toBytes32(localToken.address),
id: utils.addressToBytes32(localToken.address),
},
action: {
type: types.BridgeMessageTypes.TRANSFER,
@ -465,21 +430,21 @@ describe('BridgeRouter', async () => {
});
it('transfers tokens on prefill', async () => {
const prefillTx = await bridgeDeployment
const prefillTx = await bridge
.router(localDomain)
.preFill(transferMessage);
await expect(prefillTx)
.to.emit(localToken, 'Transfer')
.withArgs(
deployerAddress,
deployer.address,
recipient,
BigNumber.from(TOKEN_VALUE).mul(9995).div(10000),
);
});
it('unlocks tokens on message receipt', async () => {
await bridgeDeployment.router(localDomain).preFill(transferMessage);
let deliver = bridgeDeployment
await bridge.router(localDomain).preFill(transferMessage);
let deliver = bridge
.router(localDomain)
.handle(remoteDomain, deployerId, transferMessage, {
gasLimit: PROTOCOL_PROCESS_GAS,
@ -487,8 +452,8 @@ describe('BridgeRouter', async () => {
await expect(deliver)
.to.emit(localToken, 'Transfer')
.withArgs(
bridgeDeployment.router(localDomain).address,
deployerAddress,
bridge.router(localDomain).address,
deployer.address,
TOKEN_VALUE,
);
});
@ -515,7 +480,7 @@ describe('BridgeRouter', async () => {
const requestMessageObj: types.Message = {
tokenId: {
domain: localDomain,
id: utils.toBytes32(localToken.address),
id: utils.addressToBytes32(localToken.address),
},
action: {
type: types.BridgeMessageTypes.REQUEST_DETAILS,
@ -526,7 +491,7 @@ describe('BridgeRouter', async () => {
const outgoingDetailsObj: types.Message = {
tokenId: {
domain: localDomain,
id: utils.toBytes32(localToken.address),
id: utils.addressToBytes32(localToken.address),
},
action: {
type: types.BridgeMessageTypes.DETAILS,
@ -560,19 +525,15 @@ describe('BridgeRouter', async () => {
incomingDetails = serializeMessage(incomingDetailsObj);
// first send in a transfer to create the repr
await bridgeDeployment
await bridge
.router(localDomain)
.handle(remoteDomain, deployerId, transferMessage);
repr = await bridgeDeployment.bridgeToken(
localDomain,
remoteDomain,
testToken,
);
repr = await bridge.bridgeToken(localDomain, remoteDomain, testToken);
});
it('allows admins to dispatch requestDetails', async () => {
const requestTx = await bridgeDeployment
const requestTx = await bridge
.router(localDomain)
.requestDetails(remoteDomain, testToken);
@ -584,28 +545,22 @@ describe('BridgeRouter', async () => {
};
const requestDetails = serializeMessage(requestDetailsObj);
await expect(requestTx).to.emit(
abacusDeployment.outbox(localDomain),
'Dispatch',
);
await expect(requestTx).to.emit(abacus.outbox(localDomain), 'Dispatch');
});
it('handles incoming requestDetails by dispatching a details message', async () => {
const handleTx = bridgeDeployment
const handleTx = bridge
.router(localDomain)
.handle(remoteDomain, deployerId, requestMessage);
await expect(handleTx).to.emit(
abacusDeployment.outbox(localDomain),
'Dispatch',
);
await expect(handleTx).to.emit(abacus.outbox(localDomain), 'Dispatch');
});
it('errors if token is a repr', async () => {
const badRequestObj: types.Message = {
tokenId: {
domain: localDomain,
id: utils.toBytes32(repr.address),
id: utils.addressToBytes32(repr.address),
},
action: {
type: types.BridgeMessageTypes.REQUEST_DETAILS,
@ -613,7 +568,7 @@ describe('BridgeRouter', async () => {
};
const badRequest = serializeMessage(badRequestObj);
let badRequestTx = bridgeDeployment
let badRequestTx = bridge
.router(localDomain)
.handle(remoteDomain, deployerId, badRequest);
@ -624,7 +579,7 @@ describe('BridgeRouter', async () => {
const badRequestObj: types.Message = {
tokenId: {
domain: localDomain,
id: utils.toBytes32(localToken.address),
id: utils.addressToBytes32(localToken.address),
},
action: {
type: types.BridgeMessageTypes.REQUEST_DETAILS,
@ -632,7 +587,7 @@ describe('BridgeRouter', async () => {
};
const badRequest = serializeMessage(badRequestObj);
let badRequestTx = bridgeDeployment
let badRequestTx = bridge
.router(localDomain)
.handle(3812, deployerId, badRequest);
@ -645,7 +600,7 @@ describe('BridgeRouter', async () => {
expect((await repr.symbol()).length).to.equal(15);
expect(await repr.decimals()).to.equal(18);
await bridgeDeployment
await bridge
.router(localDomain)
.handle(remoteDomain, deployerId, incomingDetails);
@ -674,43 +629,39 @@ describe('BridgeRouter', async () => {
transferMessage = serializeMessage(transferMessageObj);
// first send in a transfer to create the repr
await bridgeDeployment
await bridge
.router(localDomain)
.handle(remoteDomain, deployerId, transferMessage);
defaultRepr = await bridgeDeployment.bridgeToken(
defaultRepr = await bridge.bridgeToken(
localDomain,
remoteDomain,
testToken,
);
expect(await defaultRepr.balanceOf(deployerAddress)).to.equal(
expect(await defaultRepr.balanceOf(deployer.address)).to.equal(
BigNumber.from(VALUE),
);
// setup custom
customRepr = await new BridgeToken__factory(deployer).deploy();
await customRepr.initialize();
expect(await customRepr.balanceOf(deployerAddress)).to.equal(
expect(await customRepr.balanceOf(deployer.address)).to.equal(
BigNumber.from(0),
);
});
it('migrate errors if old === new', async () => {
const migrate = bridgeDeployment
.router(localDomain)
.migrate(defaultRepr.address);
const migrate = bridge.router(localDomain).migrate(defaultRepr.address);
await expect(migrate).to.be.revertedWith('!different');
});
it('migrate errors if custom token is not enrolled', async () => {
const migrate = bridgeDeployment
.router(localDomain)
.migrate(customRepr.address);
const migrate = bridge.router(localDomain).migrate(customRepr.address);
await expect(migrate).to.be.revertedWith('!repr');
});
it('errors if no mint/burn privileges', async () => {
const enrollTx = bridgeDeployment
const enrollTx = bridge
.router(localDomain)
.enrollCustom(remoteDomain, testToken, customRepr.address);
@ -720,11 +671,9 @@ describe('BridgeRouter', async () => {
});
describe('when registering a custom token', () => {
beforeEach(async () => {
await customRepr.transferOwnership(
bridgeDeployment.router(localDomain).address,
);
await customRepr.transferOwnership(bridge.router(localDomain).address);
const enrollTx = bridgeDeployment
const enrollTx = bridge
.router(localDomain)
.enrollCustom(remoteDomain, testToken, customRepr.address);
@ -733,22 +682,17 @@ describe('BridgeRouter', async () => {
it('registers the custom token', async () => {
expect(
(
await bridgeDeployment.bridgeToken(
localDomain,
remoteDomain,
testToken,
)
).address,
(await bridge.bridgeToken(localDomain, remoteDomain, testToken))
.address,
).to.equal(customRepr.address);
let [domain, token] = await bridgeDeployment
let [domain, token] = await bridge
.router(localDomain)
.getCanonicalAddress(customRepr.address);
expect(domain).to.equal(remoteDomain);
expect(token).to.equal(testToken);
[domain, token] = await bridgeDeployment
[domain, token] = await bridge
.router(localDomain)
.getCanonicalAddress(defaultRepr.address);
expect(domain).to.equal(remoteDomain);
@ -758,20 +702,20 @@ describe('BridgeRouter', async () => {
describe('when bridging a custom token', () => {
let defaultBalance: BigNumber;
beforeEach(async () => {
defaultBalance = await defaultRepr.balanceOf(deployerAddress);
defaultBalance = await defaultRepr.balanceOf(deployer.address);
// first send in a transfer to create the repr
await bridgeDeployment
await bridge
.router(localDomain)
.handle(remoteDomain, deployerId, transferMessage);
});
it('mints incoming tokens in the custom repr', async () => {
// did not mint default
expect(await defaultRepr.balanceOf(deployerAddress)).to.equal(
expect(await defaultRepr.balanceOf(deployer.address)).to.equal(
defaultBalance,
);
// did mint custom
expect(await customRepr.balanceOf(deployerAddress)).to.equal(
expect(await customRepr.balanceOf(deployer.address)).to.equal(
BigNumber.from(VALUE),
);
});
@ -788,37 +732,39 @@ describe('BridgeRouter', async () => {
};
const smallTransferMessage = serializeMessage(smallTransfer);
const defaultSendTx = await bridgeDeployment
const defaultSendTx = await bridge
.router(localDomain)
.send(defaultRepr.address, TOKEN_VALUE, remoteDomain, deployerId);
await expect(defaultSendTx).to.emit(
abacusDeployment.outbox(localDomain),
abacus.outbox(localDomain),
'Dispatch',
);
const customSendTx = await bridgeDeployment
const customSendTx = await bridge
.router(localDomain)
.send(customRepr.address, TOKEN_VALUE, remoteDomain, deployerId);
await expect(customSendTx).to.emit(
abacusDeployment.outbox(localDomain),
abacus.outbox(localDomain),
'Dispatch',
);
});
it('allows users to migrate', async () => {
const defaultBalance = await defaultRepr.balanceOf(deployerAddress);
const customBalance = await customRepr.balanceOf(deployerAddress);
const defaultBalance = await defaultRepr.balanceOf(
deployer.address,
);
const customBalance = await customRepr.balanceOf(deployer.address);
let migrateTx = bridgeDeployment
let migrateTx = bridge
.router(localDomain)
.migrate(defaultRepr.address);
await expect(migrateTx).to.not.be.reverted;
expect(await defaultRepr.balanceOf(deployerAddress)).to.equal(
expect(await defaultRepr.balanceOf(deployer.address)).to.equal(
ethers.constants.Zero,
);
expect(await customRepr.balanceOf(deployerAddress)).to.equal(
expect(await customRepr.balanceOf(deployer.address)).to.equal(
defaultBalance.add(customBalance),
);
});

@ -1,8 +1,8 @@
import { ethers } from 'hardhat';
import { BytesLike } from 'ethers';
import { expect } from 'chai';
import { utils } from '@abacus-network/utils';
import { utils } from '@abacus-network/abacus-sol/test';
import {
BridgeMessageTypes,
TokenIdentifier,
@ -45,7 +45,7 @@ describe('BridgeMessage', async () => {
before(async () => {
const [deployer] = await ethers.getSigners();
deployerAddress = await deployer.getAddress();
const deployerId = utils.toBytes32(deployerAddress).toLowerCase();
const deployerId = utils.addressToBytes32(deployerAddress);
const TOKEN_VALUE = 0xffff;
// tokenId

@ -3,8 +3,7 @@ import { assert } from 'chai';
import * as ethers from 'ethers';
import { AbacusDeployment } from '@abacus-network/abacus-sol/test/lib/AbacusDeployment';
import { toBytes32 } from '@abacus-network/abacus-sol/test/lib/utils';
import * as types from '@abacus-network/abacus-sol/test/lib/types';
import { utils, types } from '@abacus-network/utils';
import {
MockWeth__factory,
@ -56,7 +55,7 @@ export class BridgeDeployment {
if (local !== remote) {
await instances[local].router.enrollRemoteRouter(
remote,
toBytes32(instances[remote].router.address),
utils.addressToBytes32(instances[remote].router.address),
);
}
}

@ -1,18 +1,15 @@
import { ethers } from 'hardhat';
import { expect } from 'chai';
import { utils } from '@abacus-network/utils';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import {
types,
utils,
core,
AbacusDeployment,
} from '@abacus-network/abacus-sol/test';
import { core, AbacusDeployment } from '@abacus-network/abacus-sol/test';
import { GovernanceDeployment } from './lib/GovernanceDeployment';
import {
formatSetGovernor,
formatCall,
increaseTimestampBy,
} from './lib/utils';
import { GovernanceDeployment } from './lib/GovernanceDeployment';
import {
TestSet,
TestSet__factory,
@ -28,8 +25,8 @@ const domains = [localDomain, remoteDomain];
const ONLY_OWNER_REVERT_MESSAGE = 'Ownable: caller is not the owner';
describe('GovernanceRouter', async () => {
let governor: types.Signer,
recoveryManager: types.Signer,
let governor: SignerWithAddress,
recoveryManager: SignerWithAddress,
router: GovernanceRouter,
remote: GovernanceRouter,
testSet: TestSet,
@ -64,7 +61,7 @@ describe('GovernanceRouter', async () => {
expect(await router.governor()).to.not.equal(ethers.constants.AddressZero);
const message = formatSetGovernor(ethers.constants.AddressZero);
// Create a fake abacus message coming from the remote governance router.
const fakeMessage = core.formatMessage(
const fakeMessage = utils.formatMessage(
remoteDomain,
remote.address,
0, // nonce is ignored
@ -94,7 +91,7 @@ describe('GovernanceRouter', async () => {
it('rejects message from unenrolled router', async () => {
const message = formatSetGovernor(ethers.constants.AddressZero);
// Create a fake abacus message coming from the remote governance router.
const fakeMessage = core.formatMessage(
const fakeMessage = utils.formatMessage(
remoteDomain,
ethers.constants.AddressZero,
0, // nonce is ignored

@ -1,9 +1,7 @@
import { ethers } from 'ethers';
import {
utils,
types,
AbacusDeployment,
} from '@abacus-network/abacus-sol/test';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { utils, types } from '@abacus-network/utils';
import { AbacusDeployment } from '@abacus-network/abacus-sol/test';
import {
GovernanceRouter__factory,
@ -25,8 +23,8 @@ export class GovernanceDeployment {
static async fromAbacusDeployment(
abacus: AbacusDeployment,
governor: types.Signer,
recoveryManager: types.Signer,
governor: SignerWithAddress,
recoveryManager: SignerWithAddress,
) {
// Deploy routers.
const instances: Record<number, GovernanceInstance> = {};
@ -45,7 +43,7 @@ export class GovernanceDeployment {
for (const remote of abacus.domains) {
await instances[local].router.enrollRemoteRouter(
remote,
utils.toBytes32(instances[remote].router.address),
utils.addressToBytes32(instances[remote].router.address),
);
}
}
@ -61,8 +59,8 @@ export class GovernanceDeployment {
static async deployInstance(
domain: types.Domain,
governor: types.Signer,
recoveryManager: types.Signer,
governor: SignerWithAddress,
recoveryManager: SignerWithAddress,
connectionManagerAddress: types.Address,
): Promise<GovernanceInstance> {
const routerFactory = new GovernanceRouter__factory(governor);

@ -1,5 +1,5 @@
import { ethers } from 'ethers';
import { types, utils } from '@abacus-network/abacus-sol/test';
import { types, utils } from '@abacus-network/utils';
export enum GovernanceMessage {
CALL = 1,

@ -0,0 +1 @@
dist/

@ -0,0 +1,2 @@
export * as types from './src/types'
export * as utils from './src/utils'

@ -0,0 +1,19 @@
{
"prepublish": "npm run build",
"name": "@abacus-network/utils",
"version": "0.0.5",
"description": "Abacus utilities",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"scripts": {
"build": "tsc",
"check": "tsc --noEmit",
"prettier": "prettier --write ./src"
},
"author": "Celo Labs Inc.",
"license": "MIT OR Apache-2.0",
"dependencies": {
"chai": "^4.3.0",
"ethers": "^5.4.7"
}
}

@ -1,12 +1,10 @@
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { BytesLike } from 'ethers';
import { BytesLike } from "ethers";
/********* BASIC TYPES *********/
export type Domain = number;
export type Address = string;
export type AddressBytes32 = string;
export type HexString = string;
export type Signer = SignerWithAddress;
export type BytesArray = [
BytesLike,
BytesLike,
@ -39,7 +37,7 @@ export type BytesArray = [
BytesLike,
BytesLike,
BytesLike,
BytesLike,
BytesLike
];
/********* OPTICS CORE *********/
@ -53,3 +51,15 @@ export type CallData = {
to: Address;
data: string;
};
export enum AbacusState {
UNINITIALIZED = 0,
ACTIVE,
FAILED,
}
export enum MessageStatus {
NONE = 0,
PENDING,
PROCESSED,
}

@ -0,0 +1,75 @@
import { assert } from "chai";
import { ethers } from "ethers";
import { Domain, Address, HexString } from "./types";
/*
* Gets the byte length of a hex string
*
* @param hexStr - the hex string
* @return byteLength - length in bytes
*/
export function getHexStringByteLength(hexStr: string) {
let len = hexStr.length;
// check for prefix, remove if necessary
if (hexStr.slice(0, 2) == "0x") {
len -= 2;
}
// divide by 2 to get the byte length
return len / 2;
}
export const stringToBytes32 = (s: string): string => {
const str = Buffer.from(s.slice(0, 32), "utf-8");
const result = Buffer.alloc(32);
str.copy(result);
return "0x" + result.toString("hex");
};
export function addressToBytes32(address: Address): string {
return ethers.utils
.hexZeroPad(ethers.utils.hexStripZeros(address), 32)
.toLowerCase();
}
export const formatMessage = (
localDomain: Domain,
senderAddr: Address,
sequence: number,
destinationDomain: Domain,
recipientAddr: Address,
body: HexString
): string => {
senderAddr = addressToBytes32(senderAddr);
recipientAddr = addressToBytes32(recipientAddr);
return ethers.utils.solidityPack(
["uint32", "bytes32", "uint32", "uint32", "bytes32", "bytes"],
[localDomain, senderAddr, sequence, destinationDomain, recipientAddr, body]
);
};
export function messageHash(message: HexString): string {
return ethers.utils.solidityKeccak256(["bytes"], [message]);
}
export function destinationAndNonce(
destination: Domain,
sequence: number
): ethers.BigNumber {
assert(destination < Math.pow(2, 32) - 1);
assert(sequence < Math.pow(2, 32) - 1);
return ethers.BigNumber.from(destination)
.mul(ethers.BigNumber.from(2).pow(32))
.add(ethers.BigNumber.from(sequence));
}
export function domainHash(domain: Number): string {
return ethers.utils.solidityKeccak256(
["uint32", "string"],
[domain, "OPTICS"]
);
}

@ -0,0 +1,12 @@
{
"compilerOptions": {
"outDir": "./dist/",
"rootDir": "./"
},
"exclude": ["./node_modules/", "./dist/", "./tmp.ts"],
"extends": "../tsconfig.package.json",
"include": [
"./index.ts",
"./src/*.ts"
]
}
Loading…
Cancel
Save