Hello world 🧮 (#1)

pull/2435/head
Asa Oines 3 years ago committed by GitHub
parent 26863d2889
commit 71f157ab8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .eslintignore
  2. 20
      .eslintrc.json
  3. 86
      .github/workflows/ci.yml
  4. 8
      .gitignore
  5. 18
      .prettierrc
  6. 3
      .solcover.js
  7. 8
      .solhint.json
  8. 60
      contracts/PingPong.sol
  9. 26
      hardhat.config.ts
  10. 40536
      package-lock.json
  11. 46
      package.json
  12. 28
      test/PingPongDeploy.ts
  13. 73
      test/pingPong.test.ts
  14. 33
      tsconfig.json

@ -0,0 +1,3 @@
node_modules
dist
coverage

@ -0,0 +1,20 @@
{
"env": {
"node": true
},
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"comma-dangle": ["error", "always-multiline"],
"semi": ["error", "always"],
"@typescript-eslint/explicit-module-boundary-types": ["error"],
"@typescript-eslint/no-non-null-assertion": ["error"],
"@typescript-eslint/no-explicit-any": ["error", { "ignoreRestArgs": true }]
}
}

@ -0,0 +1,86 @@
name: ci
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [main]
pull_request:
branches: [main]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: .//node_modules
key: ${{ runner.os }}-npm-cache-${{ hashFiles('./package-lock.json') }}
- name: npm-install
# Check out the lockfile from main, reinstall, and then
# verify the lockfile matches what was committed.
run: |
npm install
CHANGES=$(git status -s)
if [[ ! -z $CHANGES ]]; then
echo "Changes found: $CHANGES"
git diff
exit 1
fi
build:
runs-on: ubuntu-latest
needs: [install]
steps:
- uses: actions/checkout@v2
- name: npm-cache
uses: actions/cache@v2
with:
path: .//node_modules
key: ${{ runner.os }}-npm-cache-${{ hashFiles('./package-lock.json') }}
- name: build-cache
uses: actions/cache@v2
with:
path: ./*
key: ${{ github.sha }}
- name: build
run: npm run build
prettier:
runs-on: ubuntu-latest
needs: [install]
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: .//node_modules
key: ${{ runner.os }}-npm-cache-${{ hashFiles('./package-lock.json') }}
- name: prettier
run: |
npm run prettier
CHANGES=$(git status -s)
if [[ ! -z $CHANGES ]]; then
echo "Changes found: $CHANGES"
exit 1
fi
test:
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: ./*
key: ${{ github.sha }}
- name: test
run: npm run test

8
.gitignore vendored

@ -0,0 +1,8 @@
artifacts/
cache/
coverage/
coverage.json
dist/
node_modules/
src/types/
*.swp

@ -0,0 +1,18 @@
{
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"overrides": [
{
"files": "*.sol",
"options": {
"printWidth": 80,
"tabWidth": 4,
"useTabs": false,
"singleQuote": false,
"bracketSpacing": false,
"explicitTypes": "always"
}
}
]
}

@ -0,0 +1,3 @@
module.exports = {
skipFiles: ["test"],
};

@ -0,0 +1,8 @@
{
"extends": "solhint:recommended",
"rules": {
"compiler-version": ["error", ">=0.6.0"],
"func-visibility": ["warn", {"ignoreConstructors":true}],
"not-rely-on-time": "off"
}
}

@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.13;
// ============ External Imports ============
import {Router} from "@abacus-network/app/contracts/Router.sol";
/*
============ PingPong ============
The PingPong app is capable of initiating PingPong "matches" between two chains.
A match consists of "volleys" sent back-and-forth between the two chains via Abacus.
The first volley in a match is always a Ping volley.
When a contract receives a Ping volley, it returns a Pong.
When a contract receives a Pong volley, it returns a Ping.
The contracts keep track of the number of volleys they've sent and received,
and emit events for each Sent and Received volley so that spectators can watch.
*/
contract PingPong is Router {
uint256 public sent;
uint256 public received;
// ============ Events ============
event Sent(uint32 indexed origin, uint32 indexed destination, bool isPing);
event Received(
uint32 indexed origin,
uint32 indexed destination,
bool isPing
);
// ============ Constructor ============
constructor(address _connectionManager) {
__Router_initialize(_connectionManager);
}
function pingRemote(uint32 _destination) external {
_send(_destination, true);
}
function _handle(
uint32 _origin,
bytes32,
bytes memory _message
) internal override {
received += 1;
bool _isPing = abi.decode(_message, (bool));
uint32 localDomain = _localDomain();
emit Received(_origin, localDomain, _isPing);
_send(_origin, !_isPing);
}
function _send(uint32 _destination, bool _isPing) internal {
sent += 1;
uint32 localDomain = _localDomain();
bytes memory message = abi.encode(_isPing);
_dispatchToRemoteRouter(_destination, message);
emit Sent(localDomain, _destination, _isPing);
}
}

@ -0,0 +1,26 @@
import '@abacus-network/hardhat';
import '@nomiclabs/hardhat-waffle';
import '@typechain/hardhat';
import 'hardhat-gas-reporter';
import 'solidity-coverage';
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: {
compilers: [
{
version: '0.8.13',
},
],
},
gasReporter: {
currency: 'USD',
},
typechain: {
outDir: './src/types',
target: 'ethers-v5',
alwaysGenerateOverloads: false, // should overloads with full signatures like deposit(uint256) be generated always, even if there are no overloads?
},
};

40536
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,46 @@
{
"name": "@abacus-network/template",
"version": "0.0.0",
"license": "MIT OR Apache-2.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"directories": {
"test": "test"
},
"scripts": {
"build:contracts": "hardhat compile && hardhat typechain",
"build:sdk": "tsc",
"build": "npm run build:contracts && npm run build:sdk",
"test:contracts": "hardhat test",
"test:sdk": "",
"test": "npm run test:contracts && npm run test:sdk",
"prettier": "prettier --write ./contracts ./test",
"coverage": "hardhat coverage"
},
"dependencies": {
"@abacus-network/app": "^0.0.2",
"@ethersproject/bignumber": "^5.5.0",
"@ethersproject/bytes": "^5.5.0",
"ethers": "^5.4.7"
},
"devDependencies": {
"@abacus-network/hardhat": "^0.0.17",
"@nomiclabs/hardhat-ethers": "^2.0.1",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@typechain/ethers-v5": "~7.0.0",
"@typechain/hardhat": "^2.0.1",
"@types/mocha": "^9.1.0",
"chai": "^4.3.0",
"eslint": "^7.20.0",
"hardhat": "^2.8.3",
"hardhat-gas-reporter": "^1.0.7",
"prettier": "^2.2.1",
"prettier-plugin-solidity": "^1.0.0-beta.5",
"solhint": "^3.3.2",
"solhint-plugin-prettier": "^0.0.5",
"solidity-coverage": "^0.7.14",
"ts-node": "^10.1.0",
"typechain": "^5.0.0",
"typescript": "^4.3.5"
}
}

@ -0,0 +1,28 @@
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { types } from '@abacus-network/utils';
import { TestAbacusDeploy, TestRouterDeploy } from '@abacus-network/hardhat';
import { PingPong__factory, PingPong } from '../src/types';
// PingPong has no configurable variables.
export type PingPongConfig = {
signer: SignerWithAddress;
};
export class PingPongDeploy extends TestRouterDeploy<PingPong, PingPongConfig> {
async deployInstance(
domain: types.Domain,
abacus: TestAbacusDeploy,
): Promise<PingPong> {
const pingPongFactory = new PingPong__factory(this.config.signer);
const router = await pingPongFactory.deploy(
abacus.xAppConnectionManager(domain).address,
);
await router.transferOwnership(this.config.signer.address);
return router;
}
router(domain: types.Domain): PingPong {
return this.instances[domain];
}
}

@ -0,0 +1,73 @@
import { ethers, abacus } from 'hardhat';
import { expect } from 'chai';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { PingPongDeploy } from './PingPongDeploy';
import { PingPong } from '../src/types';
const localDomain = 1000;
const remoteDomain = 2000;
const domains = [localDomain, remoteDomain];
describe('PingPong', async () => {
let signer: SignerWithAddress,
router: PingPong,
remote: PingPong,
pingPong: PingPongDeploy;
before(async () => {
[signer] = await ethers.getSigners();
await abacus.deploy(domains, signer);
});
beforeEach(async () => {
const config = { signer };
pingPong = new PingPongDeploy(config);
await pingPong.deploy(abacus);
router = pingPong.router(localDomain);
remote = pingPong.router(remoteDomain);
expect(await router.sent()).to.equal(0);
expect(await router.received()).to.equal(0);
expect(await remote.sent()).to.equal(0);
expect(await remote.received()).to.equal(0);
});
it('sends an initial ping', async () => {
await expect(router.pingRemote(remoteDomain)).to.emit(
abacus.outbox(localDomain),
'Dispatch',
);
expect(await router.sent()).to.equal(1);
expect(await router.received()).to.equal(0);
});
it('responds to a ping with a pong', async () => {
await router.pingRemote(remoteDomain);
// Processing the initial ping causes a pong to be dispatched from the remote domain.
await abacus.processOutboundMessages(localDomain);
// The initial ping has been dispatched.
expect(await router.sent()).to.equal(1);
// The pong has been dispatched but not processed..
expect(await router.received()).to.equal(0);
// The pong has been dispatched.
expect(await remote.sent()).to.equal(1);
// The initial ping has been processed.
expect(await remote.received()).to.equal(1);
});
it('responds to a pong with a ping', async () => {
await router.pingRemote(remoteDomain);
// Processing the initial ping causes a pong to be dispatched from the remote domain.
await abacus.processOutboundMessages(localDomain);
// Processing the pong response causes a ping to be dispatched from the local domain.
await abacus.processOutboundMessages(remoteDomain);
// The initial ping and the response to the pong.
expect(await router.sent()).to.equal(2);
// The pong.
expect(await router.received()).to.equal(1);
// The pong has been dispatched.
expect(await remote.sent()).to.equal(1);
// The initial ping has been processed.
expect(await remote.received()).to.equal(1);
});
});

@ -0,0 +1,33 @@
{
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"incremental": false,
"lib": ["es2015", "es5", "dom"],
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"preserveSymlinks": true,
"preserveWatchOutput": true,
"pretty": false,
"sourceMap": true,
"target": "es6",
"strict": true,
"outDir": "./dist/",
"rootDir": "./src/"
},
"exclude": [
"./node_modules/",
"./dist/",
"./src/types/hardhat.d.ts"
],
"include": [
"./src/"
]
}
Loading…
Cancel
Save