Hello world 🧮 (#1)
parent
26863d2889
commit
71f157ab8e
@ -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 |
@ -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?
|
||||
}, |
||||
}; |
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…
Reference in new issue