Showing preview only (878K chars total). Download the full file or copy to clipboard to get everything.
Repository: celer-network/sgn-v2-contracts
Branch: main
Commit: b8a27161e0b7
Files: 236
Total size: 794.0 KB
Directory structure:
gitextract_l6jfhm9x/
├── .eslintignore
├── .eslintrc.js
├── .github/
│ └── workflows/
│ ├── abigen.yml
│ ├── pbsol.yml
│ └── run_tests.yml
├── .gitignore
├── .prettierrc.json
├── LICENSE
├── README.md
├── benchmark/
│ └── relay.ts
├── contracts/
│ ├── circle-usdc/
│ │ ├── CircleBridgeProxy.sol
│ │ ├── CircleBridgeProxyV2.sol
│ │ └── FeeOperator.sol
│ ├── governed-owner/
│ │ ├── GovernedOwnerProxy.sol
│ │ ├── SimpleGovernance.sol
│ │ ├── customized/
│ │ │ └── MessageBusOwner.sol
│ │ ├── interfaces/
│ │ │ ├── IBridgeOwner.sol
│ │ │ ├── ICommonOwner.sol
│ │ │ ├── IMessageOwner.sol
│ │ │ ├── ISgnOwner.sol
│ │ │ └── IUpgradeableOwner.sol
│ │ └── proxies/
│ │ ├── BridgeOwnerProxy.sol
│ │ ├── CommonOwnerProxy.sol
│ │ ├── MessageOwnerProxy.sol
│ │ ├── OwnerDataTypes.sol
│ │ ├── OwnerProxyBase.sol
│ │ ├── SgnOwnerProxy.sol
│ │ └── UpgradeableOwnerProxy.sol
│ ├── integration-examples/
│ │ ├── ContractAsLP.sol
│ │ └── ContractAsSender.sol
│ ├── interfaces/
│ │ ├── IBridge.sol
│ │ ├── ICircleBridge.sol
│ │ ├── IDelayedTransfer.sol
│ │ ├── IOriginalTokenVault.sol
│ │ ├── IOriginalTokenVaultV2.sol
│ │ ├── IPeggedToken.sol
│ │ ├── IPeggedTokenBridge.sol
│ │ ├── IPeggedTokenBridgeV2.sol
│ │ ├── IPeggedTokenBurnFrom.sol
│ │ ├── IPool.sol
│ │ ├── ISigsVerifier.sol
│ │ ├── IUniswapV2.sol
│ │ ├── IWETH.sol
│ │ └── IWithdrawInbox.sol
│ ├── libraries/
│ │ ├── BridgeTransferLib.sol
│ │ ├── Pb.sol
│ │ ├── PbBridge.sol
│ │ ├── PbFarming.sol
│ │ ├── PbPegged.sol
│ │ ├── PbPool.sol
│ │ ├── PbSgn.sol
│ │ ├── PbStaking.sol
│ │ ├── Utils.sol
│ │ └── proto/
│ │ ├── README.md
│ │ ├── bridge.proto
│ │ ├── farming.proto
│ │ ├── pegged.proto
│ │ ├── pool.proto
│ │ ├── sgn.proto
│ │ └── staking.proto
│ ├── liquidity-bridge/
│ │ ├── Bridge.sol
│ │ ├── FarmingRewards.sol
│ │ ├── Pool.sol
│ │ ├── Signers.sol
│ │ └── WithdrawInbox.sol
│ ├── message/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── apps/
│ │ │ ├── RFQ.sol
│ │ │ ├── adapter/
│ │ │ │ └── MessageReceiverAdapter.sol
│ │ │ ├── examples/
│ │ │ │ ├── BatchTransfer.sol
│ │ │ │ ├── MsgExampleBasic.sol
│ │ │ │ ├── MsgExampleBasicTransfer.sol
│ │ │ │ ├── MsgExampleInOrder.sol
│ │ │ │ ├── MsgTest.sol
│ │ │ │ ├── TransferSwap.sol
│ │ │ │ └── TransferSwapSendBack.sol
│ │ │ └── nft-bridge/
│ │ │ ├── MCNNFT.sol
│ │ │ ├── NFTBridge.sol
│ │ │ ├── OrigNFT.sol
│ │ │ └── PegNFT.sol
│ │ ├── framework/
│ │ │ ├── MessageApp.sol
│ │ │ ├── MessageBusAddress.sol
│ │ │ ├── MessageReceiverApp.sol
│ │ │ └── MessageSenderApp.sol
│ │ ├── interfaces/
│ │ │ ├── IMessageBus.sol
│ │ │ └── IMessageReceiverApp.sol
│ │ ├── libraries/
│ │ │ ├── MessageSenderLib.sol
│ │ │ └── MsgDataTypes.sol
│ │ ├── messagebus/
│ │ │ ├── MessageBus.sol
│ │ │ ├── MessageBusReceiver.sol
│ │ │ └── MessageBusSender.sol
│ │ └── safeguard/
│ │ ├── DelayedMessage.sol
│ │ └── MessageAppPauser.sol
│ ├── miscs/
│ │ ├── Faucet.sol
│ │ ├── MintableERC20.sol
│ │ ├── oasys/
│ │ │ ├── L1StandardERC20.sol
│ │ │ ├── L1StandardERC20Factory.sol
│ │ │ └── README.md
│ │ └── proxy-admin/
│ │ ├── Ownable.sol
│ │ └── ProxyAdmin.sol
│ ├── pegged-bridge/
│ │ ├── OriginalTokenVault.sol
│ │ ├── OriginalTokenVaultV2.sol
│ │ ├── PeggedBrc20Bridge.sol
│ │ ├── PeggedTokenBridge.sol
│ │ ├── PeggedTokenBridgeV2.sol
│ │ ├── README.md
│ │ ├── customized/
│ │ │ └── PeggedNativeTokenBridge.sol
│ │ └── tokens/
│ │ ├── ERC20Permit/
│ │ │ ├── MintSwapCanonicalTokenPermit.sol
│ │ │ ├── MultiBridgeTokenPermit.sol
│ │ │ └── SingleBridgeTokenPermit.sol
│ │ ├── IntermediaryBridgeToken.sol
│ │ ├── IntermediaryOriginalToken.sol
│ │ ├── MintSwapCanonicalToken.sol
│ │ ├── MintSwapCanonicalTokenUpgradable.sol
│ │ ├── MultiBridgeToken.sol
│ │ ├── SingleBridgeToken.sol
│ │ ├── SwapBridgeToken.sol
│ │ ├── WrappedBridgeToken.sol
│ │ ├── customized/
│ │ │ ├── CircleBridgeToken.sol
│ │ │ ├── FraxBridgeToken.sol
│ │ │ ├── MaiBridgeToken.sol
│ │ │ ├── OntologyBridgeToken.sol
│ │ │ └── xc20/
│ │ │ ├── XC20BridgeHub.sol
│ │ │ ├── XC20BridgeToken.sol
│ │ │ └── interfaces/
│ │ │ └── IXC20BridgeHub.sol
│ │ ├── freezable/
│ │ │ ├── Freezable.sol
│ │ │ ├── MintSwapCanonicalTokenFreezable.sol
│ │ │ └── MintSwapCanonicalTokenUpgradableFreezable.sol
│ │ └── owners/
│ │ └── RestrictedMultiBridgeTokenOwner.sol
│ ├── proxy/
│ │ └── TransferAgent.sol
│ ├── safeguard/
│ │ ├── DelayedTransfer.sol
│ │ ├── Governor.sol
│ │ ├── Ownable.sol
│ │ ├── Pauser.sol
│ │ ├── VolumeControl.sol
│ │ ├── Whitelist.sol
│ │ └── sentinel/
│ │ ├── Guard.sol
│ │ ├── GuardedGovernor.sol
│ │ ├── GuardedPauser.sol
│ │ └── Sentinel.sol
│ ├── staking/
│ │ ├── DataTypes.sol
│ │ ├── Govern.sol
│ │ ├── SGN.sol
│ │ ├── Staking.sol
│ │ ├── StakingReward.sol
│ │ └── Viewer.sol
│ └── test-helpers/
│ ├── DummySwap.sol
│ ├── TestERC20.sol
│ └── WETH.sol
├── deploy/
│ ├── circle-usdc/
│ │ ├── 000_circle_bridge_proxy.ts
│ │ └── 000_circle_bridge_proxy_v2.ts
│ ├── core/
│ │ └── 000_sgn_staking.ts
│ ├── governed-owner/
│ │ ├── 000_governed_owner.ts
│ │ └── customized/
│ │ └── 000_message_bus_owner.ts
│ ├── liquidity-bridge/
│ │ ├── 000_bridge.ts
│ │ └── 001_farming_rewards.ts
│ ├── message/
│ │ ├── 000_message_bus_init.ts
│ │ ├── 001_message_bus.ts
│ │ └── apps/
│ │ ├── 000_transfer_swap.ts
│ │ ├── 001_nft_bridge.ts
│ │ ├── 002_peg_nft.ts
│ │ ├── 003_orig_nft.ts
│ │ ├── 004_mcn_nft.ts
│ │ ├── 005_msg_test.ts
│ │ ├── 006_rfq.ts
│ │ └── 007_adapter.ts
│ ├── miscs/
│ │ ├── 000_test_token.ts
│ │ ├── 001_faucet.ts
│ │ ├── 002_dummy_swap.ts
│ │ ├── 003_withdraw_inbox.ts
│ │ ├── 004_contract_as_lp.ts
│ │ ├── 005_contract_as_sender.ts
│ │ ├── 006_transfer_agent.ts
│ │ └── 007_default_proxy_admin.ts
│ ├── pegged-bridge/
│ │ ├── 000_original_token_vault.ts
│ │ ├── 001_pegged_token_bridge.ts
│ │ ├── 002_original_token_vault_v2.ts
│ │ ├── 003_pegged_token_bridge_v2.ts
│ │ ├── 004_pegged_brc20_bridge.ts
│ │ ├── customized/
│ │ │ └── 000_pegged_native_token_bridge.ts
│ │ └── tokens/
│ │ ├── 000_single_bridge_token.ts
│ │ ├── 001_single_bridge_token_permit.ts
│ │ ├── 002_multi_bridge_token.ts
│ │ ├── 003_multi_bridge_token_permit.ts
│ │ ├── 004_mint_swap_canonical_token.ts
│ │ ├── 005_mint_swap_canonical_token_permit.ts
│ │ ├── 006_mint_swap_canonical_token_upgradable.ts
│ │ ├── 007_wrapped_bridge_token.ts
│ │ ├── 008_intermediary_bridge_token.ts
│ │ ├── 009_intermediary_original_token.ts
│ │ ├── 010_circle_bridge_token.ts
│ │ ├── customized/
│ │ │ ├── 000_frax_bridge_token.ts
│ │ │ ├── 001_mai_bridge_token.ts
│ │ │ └── 002_ontology_bridge_token.ts
│ │ ├── freezable/
│ │ │ ├── 000_mint_swap_canonical_token_upgradable_freezable.ts
│ │ │ └── 001_mint_swap_canonical_token_freezable.ts
│ │ └── owners/
│ │ └── 000_restricted_multi_bridge_token_owner.ts
│ └── sentinel/
│ ├── 000_sentinel.ts
│ └── 001_sentinel_zksync.ts
├── hardhat.config.ts
├── import-sorter.json
├── package.json
├── reports/
│ ├── contract_sizes.txt
│ └── gas_usage/
│ ├── relay.txt
│ └── summary.txt
├── scripts/
│ ├── common.ts
│ ├── init_governed_owner.ts
│ ├── oasys_token_factory.ts
│ ├── pb_gen_sol.sh
│ ├── reset_signers.ts
│ ├── sentinel/
│ │ ├── sentinel_guard.ts
│ │ ├── sentinel_relax.ts
│ │ └── sentinel_set_limits.ts
│ ├── set_basics.ts
│ ├── set_limits.ts
│ ├── set_msg_fee.ts
│ ├── solc_abigen.sh
│ ├── solt.sh
│ ├── transfer_ownership.ts
│ └── update_bridge_supply_cap.ts
├── test/
│ ├── Basics.spec.ts
│ ├── Bridge.spec.ts
│ ├── FarmingRewards.spec.ts
│ ├── Governance.spec.ts
│ ├── GovernedOwner.spec.ts
│ ├── Message.spec.ts
│ ├── MultiValidator.spec.ts
│ ├── Sentinel.spec.ts
│ ├── Slash.spec.ts
│ ├── StakingReward.spec.ts
│ ├── TransferSwap.spec.ts
│ ├── ValidatorSigner.spec.ts
│ └── lib/
│ ├── common.ts
│ ├── constants.ts
│ └── proto.ts
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintignore
================================================
# folders
artifacts/
bin/
build/
cache/
deployments/
genfiles/
node_modules/
typechain/
================================================
FILE: .eslintrc.js
================================================
module.exports = {
env: {
es2021: true,
mocha: true,
node: true
},
root: true,
extends: [
'prettier',
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/errors',
'plugin:import/warnings'
],
plugins: ['@typescript-eslint'],
rules: {
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/no-use-before-define': 'error',
'arrow-body-style': 'off',
camelcase: ['error', { allow: ['^.*__factory$'] }],
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
ts: 'never'
}
],
'import/no-extraneous-dependencies': 'off',
'import/no-useless-path-segments': 'off',
'import/prefer-default-export': 'off',
'no-console': 'off',
'no-empty-function': 'off',
'no-param-reassign': 'warn',
'no-plusplus': ['off'],
'no-underscore-dangle': 'warn',
'no-unused-vars': 'off',
'no-use-before-define': 'off',
'prefer-destructuring': 'off',
'prefer-template': 'off'
},
settings: {
'import/resolver': {
node: {
extensions: ['.d.ts', '.js', '.ts']
}
}
}
};
================================================
FILE: .github/workflows/abigen.yml
================================================
# run solc/abigen for go binding
name: gen-go-binding
on:
pull_request:
paths:
- '.github/workflows/abigen.yml'
- 'hardhat.config.ts'
- 'contracts/**'
- 'scripts/**'
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.head_ref }} # so we get the topic branch to push to same PR, only available if triggered by pull request
- name: compile contracts and generate go binding
env:
PRID: ${{ github.event.number }}
BRANCH: ${{ github.head_ref }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
source scripts/solc_abigen.sh
setup_git
dld_solc && run_solc
dld_abigen && run_abigen
================================================
FILE: .github/workflows/pbsol.yml
================================================
# run pb gen sol if proto files change
name: pb-gen-sol
on:
pull_request:
paths:
- '.github/workflows/pbsol.yml'
- 'hardhat.config.ts'
- 'contracts/libraries/proto/**'
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.head_ref }} # so we get the topic branch to push to same PR, only available if triggered by pull request
- name: run pb-gen-sol and push to same PR if Pbxxx.sol files are different
run: |
source scripts/pb_gen_sol.sh
prepare_tools && gen_sol && add_to_pr
================================================
FILE: .github/workflows/run_tests.yml
================================================
name: run-tests
on:
pull_request:
paths:
- '.github/workflows/run_tests.yml'
- 'hardhat.config.ts'
- 'contracts/**'
- 'test/**'
jobs:
test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 22.3.0
- name: Installing dependencies
run: yarn install --frozen-lockfile
- name: Running tests
run: yarn test
================================================
FILE: .gitignore
================================================
artifacts/
artifacts-zk/
bin/
build/
cache/
cache-zk/
deployments/hardhat/
genfiles/
failing_metadata/
node_modules/
typechain/
*.pid
*.log
*.new
*.bak
.DS_Store
.vscode/
.idea/
.env
solc-input-*.json
flattened-*.sol
memo
================================================
FILE: .prettierrc.json
================================================
{
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"overrides": [
{
"files": "*.sol",
"options": {
"printWidth": 120,
"tabWidth": 4,
"useTabs": false,
"singleQuote": false,
"bracketSpacing": false,
"explicitTypes": "always"
}
},
{
"files": ["*.js", "*.ts", "*.md"],
"options": {
"printWidth": 120
}
}
]
}
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
================================================
FILE: README.md
================================================
# SGN Contracts
Contracts for the Celer State Guardian Network (SGN) V2.
### Run unit tests
```sh
yarn test
```
### Benchmark gas cost
```sh
yarn report-gas:benchmark
yarn report-gas:summary
```
Check `reports/gas_usage`.
### Update contract sizes
```sh
yarn size-contracts
```
Check `reports/contract_sizes.txt`.
## Deployments
### Deployment Management
Contract deployments are tracked by hardhat through the files under ./deployments directory on deployment branches.
To deploy newest contracts to mainnet, staging, or testnet chains:
1. `git checkout mainnet-deployment|staging-deployment|testnet-deployment`, correspondingly
2. `git merge main` into the deployment branch
3. deploy the contracts
4. push the deployments file changes
If any contracts (e.g. libraries) are used for both mainnet and staging, follow the step above to deploy them on staging chains first, then cherry-pick the commit containing ONLY the deployment changes of these shared contracts to `mainnet-deployment`. Please be cautious with file changes when doing such operation.
Rules:
1. ./deployments should NOT exist on main branch
2. only merge main into the deployment branches
3. only change the ./deployments directory on deployment branches so that there will always be no conflicts when merge main
### Deploy contracts
1. `cp .env.template .env`, then ensure all environment variables are set in `.env`.
2. Replace `INFURA-PROJECT-ID` suffix of the network endpoint in `.env`, that you're going to use.
3. Add private key of your account that would be used, in `.env`. Refer to `hardhat.config.ts` for env param key.
4. Deploy SGN and Staking contracts:
```sh
hardhat deploy --network <network> --tags SGNStaking
```
Deploy Bridge contract:
```sh
hardhat deploy --network <network> --tags Bridge
```
Deploy OriginalTokenVault contract:
Make sure to set ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER in .env to the Bridge address when deploying.
Such as:
ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER=0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22
Where 0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22 is the Bridge contract address
```sh
hardhat deploy --network <network> --tags OriginalTokenVault
```
Deploy PeggedTokenBridge contract:
Make sure to set ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER in .env to the Bridge address when deploying.
Such as:
PEGGED_TOKEN_BRIDGE_SIGS_VERIFIER=0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22
Where 0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22 is the Bridge contract address
```sh
hardhat deploy --network <network> --tags PeggedTokenBridge
```
Deploy OriginalTokenVaultV2 contract:
Make sure to set ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER in .env to the Bridge address when deploying.
Such as:
ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER=0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22
Where 0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22 is the Bridge contract address
```sh
hardhat deploy --network <network> --tags OriginalTokenVaultV2
```
Deploy PeggedTokenBridgeV2 contract:
Make sure to set ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER in .env to the Bridge address when deploying.
Such as:
PEGGED_TOKEN_BRIDGE_SIGS_VERIFIER=0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22
Where 0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22 is the Bridge contract address
```sh
hardhat deploy --network <network> --tags PeggedTokenBridgeV2
```
### Verify contracts on explorers
#### On Etherscan variants via hardhat etherscan-verify
This is the recommended way for most mainnet Etherscan variants.
Make sure the `ETHERSCAN_API_KEY` is set correctly in `.env`.
```sh
hardhat etherscan-verify --network <network> --license "GPL-3.0" --force-license
```
#### On Etherscan variants via solt
This is useful since most testnet Etherscan variants don't offer verification via the API.
1. Generate the standard JSON input files:
```sh
source scripts/solt.sh
run_solt_write
```
2. Then try:
```sh
solt verify --license 5 --network <network> solc-input-<contract>.json <deployed address> <contract name>
```
3. If the second step fails, go to Etherscan and manually verify using the standard JSON input files.
#### On Blockscout variants via sourcify
This is used if the Blockscout variant requires "Sources and Metadata JSON".
```sh
hardhat sourcify --network <network>
```
#### On Blockscout variants via flattened source files
This is used if the Blockscout variant requires a single source file, or in general as a last resort.
1. Flatten the source files:
```sh
hardhat flatten <path-to-contract> > flattened.sol
```
2. Edit `flattened.sol`. Remove the duplicate `SPDX-License-Identifier` lines, keeping a single copy of
```
// SPDX-License-Identifier: GPL-3.0-only
```
and submit to Blockscout.
Sometimes you also need to remove the duplicate `pragma solidity` lines.
## Upgradable contract via the proxy pattern
### How it works
proxy contract holds state and delegatecall all calls to actual impl contract. When upgrade, a new impl contract is deployed, and proxy is updated to point to the new contract. below from [openzeppelin doc](https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#upgrading-via-the-proxy-pattern)
```
User ---- tx ---> Proxy ----------> Implementation_v0
|
------------> Implementation_v1
|
------------> Implementation_v2
```
### Add upgradable contract
To minimize code fork, we add a new contract that inherits existing contract, eg. `contract TokenUpgradable is Token`. Next we need to ensure that all states set in Token contract constructor (and its parent contracts) must be settable via a separate normal func like `init`. This will allow Proxy contract to delegeteCall init and set proper values in Proxy's state, not the impl contract state. See MintSwapCanonicalTokenUpgradable.sol for example. We also need to either shadow Ownable._owner because when proxy delegateCall, in proxy state, Ownable._owner is not set and there is no other way to set it. Or use our own Ownable.sol which has internal func initOwner
### Add deploy scripts
add a new ts file for deploy, in deploy options, add proxy section, make sure the methodName and args match actual upgradable contract
```ts
proxy: {
proxyContract: "OptimizedTransparentProxy",
execute: {
// only called when proxy is deployed, it'll call Token contract.init
// with proper args
init: {
methodName: 'init',
args: [
process.env.MINT_SWAP_CANONICAL_TOKEN_NAME,
process.env.MINT_SWAP_CANONICAL_TOKEN_SYMBOL]
}
}
}
```
see deploy/pegged/tokens/008_mint_swap_canonical_token_upgradable.ts for example
### Deploy and upgrade
hardhat deploy plugin tries to be smart and deploy ProxyAdmin only once for each chain, deploy impl contract then proxy contract
================================================
FILE: benchmark/relay.ts
================================================
import '@nomicfoundation/hardhat-ethers';
import { parseUnits, solidityPackedKeccak256, toNumber, Wallet } from 'ethers';
import fs from 'fs';
import { ethers } from 'hardhat';
import path from 'path';
import { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';
import { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';
import { deployBridgeContracts, getAccounts } from '../test/lib/common';
import { getRelayRequest } from '../test/lib/proto';
import { Bridge, TestERC20 } from '../typechain';
const GAS_USAGE_DIR = 'reports/gas_usage/';
const GAS_USAGE_LOG = path.join(GAS_USAGE_DIR, 'relay.txt');
describe('Relay Gas Benchmark', function () {
if (!fs.existsSync(GAS_USAGE_DIR)) {
fs.mkdirSync(GAS_USAGE_DIR, { recursive: true });
}
fs.rmSync(GAS_USAGE_LOG, { force: true });
fs.appendFileSync(GAS_USAGE_LOG, '<validatorNum, quorumSigNum, gasCost> for cbr testErc20 relay tx\n\n');
async function fixture() {
const [admin] = await ethers.getSigners();
const { bridge, token } = await deployBridgeContracts(admin);
return { admin, bridge, token };
}
let bridge: Bridge;
let token: TestERC20;
let admin: HardhatEthersSigner;
let accounts: Wallet[];
beforeEach(async () => {
const res = await loadFixture(fixture);
bridge = res.bridge;
token = res.token;
admin = res.admin;
accounts = await getAccounts(admin, [token], 21);
await token.transfer(bridge.getAddress(), parseUnits('1000000'));
await bridge.setEpochVolumeCaps([token.getAddress()], [parseUnits('100')]);
await bridge.setEpochLength(5);
await bridge.setDelayThresholds([token.getAddress()], [parseUnits('100')]);
});
it('benchmark relay gas cost for bridge', async function () {
let perSigCost;
for (let i = 5; i <= 21; i += 2) {
perSigCost = await doBenchmarkRelaySig(i);
}
fs.appendFileSync(GAS_USAGE_LOG, 'per sig cost: ' + perSigCost + '\n');
fs.appendFileSync(GAS_USAGE_LOG, '\n');
const perSignerCost = await doBenchmarkRelaySigner(21, 8);
fs.appendFileSync(GAS_USAGE_LOG, 'per validator cost: ' + perSignerCost + '\n');
});
async function getPowers(
accounts: Wallet[],
signerNum: number,
quorumSigNum: number
): Promise<{ signers: Wallet[]; addrs: string[]; powers: bigint[] }> {
const signers: Wallet[] = [];
const addrs: string[] = [];
const powers: bigint[] = [];
for (let i = 0; i < signerNum; i++) {
signers.push(accounts[i]);
addrs.push(accounts[i].address);
if (i == quorumSigNum - 1) {
powers.push(parseUnits('100'));
} else {
powers.push(parseUnits('1'));
}
}
return { signers, addrs, powers };
}
async function doBenchmarkRelaySig(signerNum: number) {
let firstCost = 0;
let lastCost = 0;
const maxQuorumSigNum = ((signerNum * 2) / 3 + 1) | 0;
for (let i = 3; i <= maxQuorumSigNum; i += 2) {
const gasUsed = await doBenchmarkRelay(signerNum, i);
if (i == 3) {
firstCost = toNumber(gasUsed);
}
lastCost = toNumber(gasUsed);
}
const perSigCost = Math.ceil((lastCost - firstCost) / (maxQuorumSigNum - 3));
return perSigCost;
}
async function doBenchmarkRelaySigner(maxSignerNum: number, quorumSigNum: number) {
let firstCost = 0;
let lastCost = 0;
const minSignerNum = ((quorumSigNum * 3) / 2) | 0;
for (let i = minSignerNum; i <= maxSignerNum; i++) {
const gasUsed = await doBenchmarkRelay(i, quorumSigNum);
if (i == minSignerNum) {
firstCost = toNumber(gasUsed);
}
lastCost = toNumber(gasUsed);
}
const perSignerCost = Math.ceil((lastCost - firstCost) / (maxSignerNum - minSignerNum));
return perSignerCost;
}
async function doBenchmarkRelay(signerNum: number, quorumSigNum: number) {
if (quorumSigNum > signerNum) {
quorumSigNum = signerNum;
}
const { signers, addrs, powers } = await getPowers(accounts, signerNum, quorumSigNum);
await bridge.notifyResetSigners();
await bridge.resetSigners(addrs, powers);
const sender = accounts[0];
const receiver = accounts[1];
const amount = parseUnits('1');
const chainId = toNumber((await ethers.provider.getNetwork()).chainId);
const srcXferId = solidityPackedKeccak256(['uint64'], [Date.now()]); // fake src xfer id
const { relayBytes, sigs } = await getRelayRequest(
sender.address,
receiver.address,
await token.getAddress(),
amount,
chainId,
chainId,
srcXferId,
signers,
await bridge.getAddress()
);
const gasUsed = (await (await bridge.relay(relayBytes, sigs, addrs, powers)).wait())!.gasUsed;
fs.appendFileSync(GAS_USAGE_LOG, signerNum.toString() + '\t' + quorumSigNum.toString() + '\t' + gasUsed + '\n');
return gasUsed;
}
});
================================================
FILE: contracts/circle-usdc/CircleBridgeProxy.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./FeeOperator.sol";
import "../interfaces/ICircleBridge.sol";
import "../safeguard/Governor.sol";
import "../safeguard/Pauser.sol";
contract CircleBridgeProxy is FeeOperator, Governor, Pauser, ReentrancyGuard {
using SafeERC20 for IERC20;
address public immutable circleBridge;
uint32 public feePercGlobal; //in 1e6
// chainId => feePercOverride, support override fee perc by dst chain
mapping(uint64 => uint32) public feePercOverride;
/// per dest chain id executor fee in this chain's USDC token
mapping(uint64 => uint256) public dstTxFee;
// 0 is regarded as not registered. Set to a negative value if target domain is actually 0.
mapping(uint64 => int32) public chidToDomain;
event FeePercUpdated(uint64[] chainIds, uint32[] feePercs);
event TxFeeUpdated(uint64[] chainIds, uint256[] fees);
event ChidToDomainUpdated(uint64[] chainIds, int32[] domains);
event Deposited(
address sender,
bytes32 recipient,
uint64 dstChid,
uint256 amount,
uint256 txFee,
uint256 percFee,
uint64 nonce
);
constructor(address _circleBridge, address _feeCollector) FeeOperator(_feeCollector) {
circleBridge = _circleBridge;
}
function depositForBurn(
uint256 _amount,
uint64 _dstChid,
bytes32 _mintRecipient,
address _burnToken
) external nonReentrant whenNotPaused returns (uint64 _nonce) {
int32 dstDomain = chidToDomain[_dstChid];
require(dstDomain != 0, "dst domain not registered");
if (dstDomain < 0) {
dstDomain = 0; // a negative value indicates the target domain is 0 actually.
}
(uint256 fee, uint256 txFee, uint256 percFee) = totalFee(_amount, _dstChid);
require(_amount > fee, "fee not covered");
IERC20(_burnToken).safeTransferFrom(msg.sender, address(this), _amount);
uint256 bridgeAmt = _amount - fee;
IERC20(_burnToken).safeIncreaseAllowance(circleBridge, bridgeAmt);
_nonce = ICircleBridge(circleBridge).depositForBurn(bridgeAmt, uint32(dstDomain), _mintRecipient, _burnToken);
IERC20(_burnToken).safeApprove(circleBridge, 0);
emit Deposited(msg.sender, _mintRecipient, _dstChid, _amount, txFee, percFee, _nonce);
}
function totalFee(uint256 _amount, uint64 _dstChid)
public
view
returns (
uint256 _fee,
uint256 _txFee,
uint256 _percFee
)
{
uint32 feePerc = feePercOverride[_dstChid];
if (feePerc == 0) {
feePerc = feePercGlobal;
}
_txFee = dstTxFee[_dstChid];
_percFee = (_amount * feePerc) / 1e6;
_fee = _txFee + _percFee;
}
function setFeePerc(uint64[] calldata _chainIds, uint32[] calldata _feePercs) external onlyGovernor {
require(_chainIds.length == _feePercs.length, "length mismatch");
for (uint256 i = 0; i < _chainIds.length; i++) {
require(_feePercs[i] < 1e6, "fee percentage too large");
if (_chainIds[i] == 0) {
feePercGlobal = _feePercs[i];
} else {
feePercOverride[_chainIds[i]] = _feePercs[i];
}
}
emit FeePercUpdated(_chainIds, _feePercs);
}
function setTxFee(uint64[] calldata _chainIds, uint256[] calldata _fees) external onlyGovernor {
require(_chainIds.length == _fees.length, "length mismatch");
for (uint256 i = 0; i < _chainIds.length; i++) {
dstTxFee[_chainIds[i]] = _fees[i];
}
emit TxFeeUpdated(_chainIds, _fees);
}
function setChidToDomain(uint64[] calldata _chainIds, int32[] calldata _domains) external onlyGovernor {
require(_chainIds.length == _domains.length, "length mismatch");
for (uint256 i = 0; i < _chainIds.length; i++) {
chidToDomain[_chainIds[i]] = _domains[i];
}
emit ChidToDomainUpdated(_chainIds, _domains);
}
}
================================================
FILE: contracts/circle-usdc/CircleBridgeProxyV2.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./FeeOperator.sol";
import "../interfaces/ICircleBridge.sol";
import "../safeguard/Governor.sol";
import "../safeguard/Pauser.sol";
contract CircleBridgeProxyV2 is FeeOperator, Governor, Pauser, ReentrancyGuard {
using SafeERC20 for IERC20;
address public immutable circleBridge;
uint32 public feePercGlobal; //in 1e6
// chainId => feePercOverride, support override fee perc by dst chain
mapping(uint64 => uint32) public feePercOverride;
/// per dest chain id executor fee in this chain's USDC token
mapping(uint64 => uint256) public dstTxFee;
// 0 is regarded as not registered. Set to a negative value if target domain is actually 0.
mapping(uint64 => int32) public chidToDomain;
event FeePercUpdated(uint64[] chainIds, uint32[] feePercs);
event TxFeeUpdated(uint64[] chainIds, uint256[] fees);
event ChidToDomainUpdated(uint64[] chainIds, int32[] domains);
event Deposited(
address sender,
bytes32 recipient,
uint64 dstChid,
uint256 amount,
uint256 txFee,
uint256 percFee,
uint32 minFinalityThreshold
);
constructor(address _circleBridge, address _feeCollector) FeeOperator(_feeCollector) {
circleBridge = _circleBridge;
}
function depositForBurn(
uint256 _amount,
uint64 _dstChid,
bytes32 _mintRecipient,
address _burnToken,
uint256 _maxFee,
uint32 _minFinalityThreshold
) external nonReentrant whenNotPaused {
int32 dstDomain = chidToDomain[_dstChid];
require(dstDomain != 0, "dst domain not registered");
if (dstDomain < 0) {
dstDomain = 0; // a negative value indicates the target domain is 0 actually.
}
(uint256 fee, uint256 txFee, uint256 percFee) = totalFee(_amount, _dstChid);
require(_amount > fee, "fee not covered");
IERC20(_burnToken).safeTransferFrom(msg.sender, address(this), _amount);
uint256 bridgeAmt = _amount - fee;
IERC20(_burnToken).safeIncreaseAllowance(circleBridge, bridgeAmt);
ICircleBridge(circleBridge).depositForBurn(bridgeAmt, uint32(dstDomain), _mintRecipient, _burnToken, bytes32(0), _maxFee, _minFinalityThreshold);
IERC20(_burnToken).safeApprove(circleBridge, 0);
emit Deposited(msg.sender, _mintRecipient, _dstChid, _amount, txFee, percFee, _minFinalityThreshold);
}
function totalFee(uint256 _amount, uint64 _dstChid)
public
view
returns (
uint256 _fee,
uint256 _txFee,
uint256 _percFee
)
{
uint32 feePerc = feePercOverride[_dstChid];
if (feePerc == 0) {
feePerc = feePercGlobal;
}
_txFee = dstTxFee[_dstChid];
_percFee = (_amount * feePerc) / 1e6;
_fee = _txFee + _percFee;
}
function setFeePerc(uint64[] calldata _chainIds, uint32[] calldata _feePercs) external onlyGovernor {
require(_chainIds.length == _feePercs.length, "length mismatch");
for (uint256 i = 0; i < _chainIds.length; i++) {
require(_feePercs[i] < 1e6, "fee percentage too large");
if (_chainIds[i] == 0) {
feePercGlobal = _feePercs[i];
} else {
feePercOverride[_chainIds[i]] = _feePercs[i];
}
}
emit FeePercUpdated(_chainIds, _feePercs);
}
function setTxFee(uint64[] calldata _chainIds, uint256[] calldata _fees) external onlyGovernor {
require(_chainIds.length == _fees.length, "length mismatch");
for (uint256 i = 0; i < _chainIds.length; i++) {
dstTxFee[_chainIds[i]] = _fees[i];
}
emit TxFeeUpdated(_chainIds, _fees);
}
function setChidToDomain(uint64[] calldata _chainIds, int32[] calldata _domains) external onlyGovernor {
require(_chainIds.length == _domains.length, "length mismatch");
for (uint256 i = 0; i < _chainIds.length; i++) {
chidToDomain[_chainIds[i]] = _domains[i];
}
emit ChidToDomainUpdated(_chainIds, _domains);
}
}
================================================
FILE: contracts/circle-usdc/FeeOperator.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../safeguard/Ownable.sol";
abstract contract FeeOperator is Ownable {
using SafeERC20 for IERC20;
address public feeCollector;
event FeeCollectorUpdated(address from, address to);
modifier onlyFeeCollector() {
require(msg.sender == feeCollector, "not fee collector");
_;
}
constructor(address _feeCollector) {
feeCollector = _feeCollector;
}
function collectFee(address[] calldata _tokens, address _to) external onlyFeeCollector {
for (uint256 i = 0; i < _tokens.length; i++) {
// use zero address to denote native token
if (_tokens[i] == address(0)) {
uint256 bal = address(this).balance;
(bool sent, ) = _to.call{value: bal, gas: 50000}("");
require(sent, "send native failed");
} else {
uint256 balance = IERC20(_tokens[i]).balanceOf(address(this));
IERC20(_tokens[i]).safeTransfer(_to, balance);
}
}
}
function setFeeCollector(address _feeCollector) external onlyOwner {
address oldFeeCollector = feeCollector;
feeCollector = _feeCollector;
emit FeeCollectorUpdated(oldFeeCollector, _feeCollector);
}
}
================================================
FILE: contracts/governed-owner/GovernedOwnerProxy.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "./proxies/CommonOwnerProxy.sol";
import "./proxies/BridgeOwnerProxy.sol";
import "./proxies/MessageOwnerProxy.sol";
import "./proxies/SgnOwnerProxy.sol";
import "./proxies/UpgradeableOwnerProxy.sol";
contract GovernedOwnerProxy is
CommonOwnerProxy,
BridgeOwnerProxy,
MessageOwnerProxy,
SgnOwnerProxy,
UpgradeableOwnerProxy
{
constructor(address _initializer) OwnerProxyBase(_initializer) {}
}
================================================
FILE: contracts/governed-owner/SimpleGovernance.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../libraries/Utils.sol";
// mainly used for governed multi owner to do infrequent owner operations,
// relatively prefer easy-to-use over gas-efficiency
contract SimpleGovernance {
uint256 public constant THRESHOLD_DECIMAL = 100;
uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour
uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks
using SafeERC20 for IERC20;
enum ParamName {
ActivePeriod,
QuorumThreshold, // default threshold for votes to pass
FastPassThreshold // lower threshold for less critical operations
}
enum ProposalType {
ExternalDefault,
ExternalFastPass,
InternalParamChange,
InternalVoterUpdate,
InternalProxyUpdate,
InternalTransferToken
}
mapping(ParamName => uint256) public params;
struct Proposal {
bytes32 dataHash; // hash(proposalType, targetAddress, calldata)
uint256 deadline;
mapping(address => bool) votes;
}
mapping(uint256 => Proposal) public proposals;
uint256 public nextProposalId;
address[] public voters;
mapping(address => uint256) public voterPowers; // voter addr -> voting power
// NOTE: proxies must be audited open-source non-upgradable contracts with following requirements:
// 1. Truthfully pass along tx sender who called the proxy function as the governance proposer.
// 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers.
// See ./proxies/CommonOwnerProxy.sol for example.
mapping(address => bool) public proposerProxies;
uint256 public nativeTokenTransferGas = 50000;
event Initiated(
address[] voters,
uint256[] powers,
address[] proxies,
uint256 activePeriod,
uint256 quorumThreshold,
uint256 fastPassThreshold
);
event ProposalCreated(
uint256 proposalId,
ProposalType proposalType,
address target,
bytes data,
uint256 deadline,
address proposer
);
event ProposalVoted(uint256 proposalId, address voter, bool vote);
event ProposalExecuted(uint256 proposalId);
event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value);
event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers);
event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops);
event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount);
constructor(
address[] memory _voters,
uint256[] memory _powers,
address[] memory _proxies,
uint256 _activePeriod,
uint256 _quorumThreshold,
uint256 _fastPassThreshold
) {
require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters");
require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period");
require(
_quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold,
"invalid init thresholds"
);
for (uint256 i = 0; i < _voters.length; i++) {
_setVoter(_voters[i], _powers[i]);
}
for (uint256 i = 0; i < _proxies.length; i++) {
proposerProxies[_proxies[i]] = true;
}
params[ParamName.ActivePeriod] = _activePeriod;
params[ParamName.QuorumThreshold] = _quorumThreshold;
params[ParamName.FastPassThreshold] = _fastPassThreshold;
emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold);
}
/*********************************
* External and Public Functions *
*********************************/
function createProposal(address _target, bytes memory _data) external returns (uint256) {
return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault);
}
// create proposal through proxy
function createProposal(
address _proposer,
address _target,
bytes memory _data,
ProposalType _type
) external returns (uint256) {
require(proposerProxies[msg.sender], "sender is not a valid proxy");
require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, "invalid type");
return _createProposal(_proposer, _target, _data, _type);
}
function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) {
bytes memory data = abi.encode(_name, _value);
uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange);
emit ParamChangeProposalCreated(proposalId, _name, _value);
return proposalId;
}
function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers)
external
returns (uint256)
{
require(_voters.length == _powers.length, "voters and powers length not match");
bytes memory data = abi.encode(_voters, _powers);
uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate);
emit VoterUpdateProposalCreated(proposalId, _voters, _powers);
return proposalId;
}
function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) {
require(_addrs.length == _ops.length, "_addrs and _ops length not match");
bytes memory data = abi.encode(_addrs, _ops);
uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate);
emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops);
return proposalId;
}
function createTransferTokenProposal(
address _receiver,
address _token,
uint256 _amount
) external returns (uint256) {
bytes memory data = abi.encode(_receiver, _token, _amount);
uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken);
emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount);
return proposalId;
}
function voteProposal(uint256 _proposalId, bool _vote) public {
require(voterPowers[msg.sender] > 0, "invalid voter");
Proposal storage p = proposals[_proposalId];
require(block.timestamp < p.deadline, "deadline passed");
p.votes[msg.sender] = _vote;
emit ProposalVoted(_proposalId, msg.sender, _vote);
}
function voteProposals(uint256[] calldata _proposalIds, bool[] calldata _votes) external {
require(_proposalIds.length == _votes.length, "proposalIds and votes length not match");
for (uint256 i = 0; i < _proposalIds.length; i++) {
voteProposal(_proposalIds[i], _votes[i]);
}
}
function executeProposal(
uint256 _proposalId,
ProposalType _type,
address _target,
bytes calldata _data
) external {
require(voterPowers[msg.sender] > 0, "only voter can execute a proposal");
Proposal storage p = proposals[_proposalId];
require(block.timestamp < p.deadline, "deadline passed");
require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match");
p.deadline = 0;
p.votes[msg.sender] = true;
(, , bool pass) = countVotes(_proposalId, _type);
require(pass, "not enough votes");
if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) {
(bool success, bytes memory res) = _target.call(_data);
require(success, Utils.getRevertMsg(res));
} else if (_type == ProposalType.InternalParamChange) {
(ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256));
params[name] = value;
if (name == ParamName.ActivePeriod) {
require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period");
} else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) {
require(
params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] &&
value < THRESHOLD_DECIMAL &&
value > 0,
"invalid threshold"
);
}
} else if (_type == ProposalType.InternalVoterUpdate) {
(address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[]));
for (uint256 i = 0; i < addrs.length; i++) {
if (powers[i] > 0) {
_setVoter(addrs[i], powers[i]);
} else {
_removeVoter(addrs[i]);
}
}
} else if (_type == ProposalType.InternalProxyUpdate) {
(address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[]));
for (uint256 i = 0; i < addrs.length; i++) {
if (ops[i]) {
proposerProxies[addrs[i]] = true;
} else {
delete proposerProxies[addrs[i]];
}
}
} else if (_type == ProposalType.InternalTransferToken) {
(address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256));
_transfer(receiver, token, amount);
}
emit ProposalExecuted(_proposalId);
}
function setNativeTokenTransferGas(uint256 _gasUsed) external {
require(voterPowers[msg.sender] > 0, "invalid caller");
nativeTokenTransferGas = _gasUsed;
}
receive() external payable {}
/**************************
* Public View Functions *
**************************/
function getVoters() public view returns (address[] memory, uint256[] memory) {
address[] memory addrs = new address[](voters.length);
uint256[] memory powers = new uint256[](voters.length);
for (uint32 i = 0; i < voters.length; i++) {
addrs[i] = voters[i];
powers[i] = voterPowers[voters[i]];
}
return (addrs, powers);
}
function getVote(uint256 _proposalId, address _voter) public view returns (bool) {
return proposals[_proposalId].votes[_voter];
}
function countVotes(uint256 _proposalId, ProposalType _type)
public
view
returns (
uint256,
uint256,
bool
)
{
uint256 yesVotes;
uint256 totalPower;
for (uint32 i = 0; i < voters.length; i++) {
if (getVote(_proposalId, voters[i])) {
yesVotes += voterPowers[voters[i]];
}
totalPower += voterPowers[voters[i]];
}
uint256 threshold;
if (_type == ProposalType.ExternalFastPass) {
threshold = params[ParamName.FastPassThreshold];
} else {
threshold = params[ParamName.QuorumThreshold];
}
bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL);
return (totalPower, yesVotes, pass);
}
/**********************************
* Internal and Private Functions *
**********************************/
// create a proposal and vote yes
function _createProposal(
address _proposer,
address _target,
bytes memory _data,
ProposalType _type
) private returns (uint256) {
require(voterPowers[_proposer] > 0, "only voter can create a proposal");
uint256 proposalId = nextProposalId;
nextProposalId += 1;
Proposal storage p = proposals[proposalId];
p.dataHash = keccak256(abi.encodePacked(_type, _target, _data));
p.deadline = block.timestamp + params[ParamName.ActivePeriod];
p.votes[_proposer] = true;
emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer);
return proposalId;
}
function _setVoter(address _voter, uint256 _power) private {
require(_power > 0, "zero power");
if (voterPowers[_voter] == 0) {
// add new voter
voters.push(_voter);
}
voterPowers[_voter] = _power;
}
function _removeVoter(address _voter) private {
require(voterPowers[_voter] > 0, "not a voter");
uint256 lastIndex = voters.length - 1;
for (uint256 i = 0; i < voters.length; i++) {
if (voters[i] == _voter) {
if (i < lastIndex) {
voters[i] = voters[lastIndex];
}
voters.pop();
voterPowers[_voter] = 0;
return;
}
}
revert("voter not found"); // this should never happen
}
function _transfer(
address _receiver,
address _token,
uint256 _amount
) private {
if (_token == address(0)) {
(bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}("");
require(sent, "failed to send native token");
} else {
IERC20(_token).safeTransfer(_receiver, _amount);
}
}
}
================================================
FILE: contracts/governed-owner/customized/MessageBusOwner.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "../../libraries/Utils.sol";
import "../interfaces/IMessageOwner.sol";
// only allow set MsgFee and PreExecuteMessageGasUsage
// disable contract upgrade or token bridge address updates
contract MessageBusOwner {
uint256 public constant THRESHOLD_DECIMAL = 100;
uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour
uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks
enum ParamName {
ActivePeriod,
QuorumThreshold // threshold for votes to pass
}
enum ProposalType {
External,
InternalParamChange,
InternalVoterUpdate
}
enum MsgFeeType {
PerByte,
Base
}
mapping(ParamName => uint256) public params;
struct Proposal {
bytes32 dataHash; // hash(proposalType, targetAddress, calldata)
uint256 deadline;
mapping(address => bool) votes;
}
mapping(uint256 => Proposal) public proposals;
uint256 public nextProposalId;
address[] public voters;
mapping(address => uint256) public voterPowers; // voter addr -> voting power
event Initiated(address[] voters, uint256[] powers, uint256 activePeriod, uint256 quorumThreshold);
event ProposalCreated(
uint256 proposalId,
ProposalType proposalType,
address target,
bytes data,
uint256 deadline,
address proposer
);
event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value);
event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers);
event SetMsgFeeProposalCreated(uint256 proposalId, address target, MsgFeeType feeType, uint256 fee);
event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage);
event ProposalVoted(uint256 proposalId, address voter, bool vote);
event ProposalExecuted(uint256 proposalId);
constructor(
address[] memory _voters,
uint256[] memory _powers,
uint256 _activePeriod,
uint256 _quorumThreshold
) {
require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters");
require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period");
require(_quorumThreshold < THRESHOLD_DECIMAL, "invalid init thresholds");
for (uint256 i = 0; i < _voters.length; i++) {
_setVoter(_voters[i], _powers[i]);
}
params[ParamName.ActivePeriod] = _activePeriod;
params[ParamName.QuorumThreshold] = _quorumThreshold;
emit Initiated(_voters, _powers, _activePeriod, _quorumThreshold);
}
/*********************************
* External and Public Functions *
*********************************/
function proposeParamChange(ParamName _name, uint256 _value) external returns (uint256) {
bytes memory data = abi.encode(_name, _value);
uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange);
emit ParamChangeProposalCreated(proposalId, _name, _value);
return proposalId;
}
function proposeVoterUpdate(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) {
require(_voters.length == _powers.length, "voters and powers length not match");
bytes memory data = abi.encode(_voters, _powers);
uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate);
emit VoterUpdateProposalCreated(proposalId, _voters, _powers);
return proposalId;
}
function proposeSetMsgFee(
address _target,
MsgFeeType _feeType,
uint256 _fee
) external returns (uint256) {
bytes4 selector;
if (_feeType == MsgFeeType.PerByte) {
selector = IMessageOwner.setFeePerByte.selector;
} else if (_feeType == MsgFeeType.Base) {
selector = IMessageOwner.setFeeBase.selector;
} else {
revert("invalid fee type");
}
bytes memory data = abi.encodeWithSelector(selector, _fee);
uint256 proposalId = _createProposal(msg.sender, _target, data, ProposalType.External);
emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee);
return proposalId;
}
function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external {
bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage);
uint256 proposalId = _createProposal(msg.sender, _target, data, ProposalType.External);
emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage);
}
function voteProposal(uint256 _proposalId, bool _vote) external {
require(voterPowers[msg.sender] > 0, "invalid voter");
Proposal storage p = proposals[_proposalId];
require(block.timestamp < p.deadline, "deadline passed");
p.votes[msg.sender] = _vote;
emit ProposalVoted(_proposalId, msg.sender, _vote);
}
function executeProposal(
uint256 _proposalId,
ProposalType _type,
address _target,
bytes calldata _data
) external {
require(voterPowers[msg.sender] > 0, "only voter can execute a proposal");
Proposal storage p = proposals[_proposalId];
require(block.timestamp < p.deadline, "deadline passed");
require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match");
p.deadline = 0;
p.votes[msg.sender] = true;
(, , bool pass) = countVotes(_proposalId);
require(pass, "not enough votes");
if (_type == ProposalType.External) {
(bool success, bytes memory res) = _target.call(_data);
require(success, Utils.getRevertMsg(res));
} else if (_type == ProposalType.InternalParamChange) {
(ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256));
params[name] = value;
if (name == ParamName.ActivePeriod) {
require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period");
} else if (name == ParamName.QuorumThreshold) {
require(value < THRESHOLD_DECIMAL && value > 0, "invalid threshold");
}
} else if (_type == ProposalType.InternalVoterUpdate) {
(address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[]));
for (uint256 i = 0; i < addrs.length; i++) {
if (powers[i] > 0) {
_setVoter(addrs[i], powers[i]);
} else {
_removeVoter(addrs[i]);
}
}
}
emit ProposalExecuted(_proposalId);
}
/**************************
* Public View Functions *
**************************/
function getVoters() public view returns (address[] memory, uint256[] memory) {
address[] memory addrs = new address[](voters.length);
uint256[] memory powers = new uint256[](voters.length);
for (uint32 i = 0; i < voters.length; i++) {
addrs[i] = voters[i];
powers[i] = voterPowers[voters[i]];
}
return (addrs, powers);
}
function getVote(uint256 _proposalId, address _voter) public view returns (bool) {
return proposals[_proposalId].votes[_voter];
}
function countVotes(uint256 _proposalId)
public
view
returns (
uint256,
uint256,
bool
)
{
uint256 yesVotes;
uint256 totalPower;
for (uint32 i = 0; i < voters.length; i++) {
if (getVote(_proposalId, voters[i])) {
yesVotes += voterPowers[voters[i]];
}
totalPower += voterPowers[voters[i]];
}
uint256 threshold = params[ParamName.QuorumThreshold];
bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL);
return (totalPower, yesVotes, pass);
}
/**********************************
* Internal and Private Functions *
**********************************/
// create a proposal and vote yes
function _createProposal(
address _proposer,
address _target,
bytes memory _data,
ProposalType _type
) private returns (uint256) {
require(voterPowers[_proposer] > 0, "only voter can create a proposal");
uint256 proposalId = nextProposalId;
nextProposalId += 1;
Proposal storage p = proposals[proposalId];
p.dataHash = keccak256(abi.encodePacked(_type, _target, _data));
p.deadline = block.timestamp + params[ParamName.ActivePeriod];
p.votes[_proposer] = true;
emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer);
return proposalId;
}
function _setVoter(address _voter, uint256 _power) private {
require(_power > 0, "zero power");
if (voterPowers[_voter] == 0) {
// add new voter
voters.push(_voter);
}
voterPowers[_voter] = _power;
}
function _removeVoter(address _voter) private {
require(voterPowers[_voter] > 0, "not a voter");
uint256 lastIndex = voters.length - 1;
for (uint256 i = 0; i < voters.length; i++) {
if (voters[i] == _voter) {
if (i < lastIndex) {
voters[i] = voters[lastIndex];
}
voters.pop();
voterPowers[_voter] = 0;
return;
}
}
revert("voter not found"); // this should never happen
}
}
================================================
FILE: contracts/governed-owner/interfaces/IBridgeOwner.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IBridgeOwner {
// for bridges
function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external;
function notifyResetSigners() external;
function increaseNoticePeriod(uint256 _period) external;
function setWrap(address _token) external;
function setSupply(address _token, uint256 _supply) external;
function increaseSupply(address _token, uint256 _delta) external;
function decreaseSupply(address _token, uint256 _delta) external;
function addGovernor(address _account) external;
function removeGovernor(address _account) external;
// for bridge tokens
function updateBridge(address _bridge) external;
function updateBridgeSupplyCap(address _bridge, uint256 _cap) external;
function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external;
}
================================================
FILE: contracts/governed-owner/interfaces/ICommonOwner.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface ICommonOwner {
function transferOwnership(address _newOwner) external;
function addPauser(address _account) external;
function removePauser(address _account) external;
}
================================================
FILE: contracts/governed-owner/interfaces/IMessageOwner.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IMessageOwner {
function setFeePerByte(uint256 _fee) external;
function setFeeBase(uint256 _fee) external;
function setLiquidityBridge(address _addr) external;
function setPegBridge(address _addr) external;
function setPegVault(address _addr) external;
function setPegBridgeV2(address _addr) external;
function setPegVaultV2(address _addr) external;
function setPreExecuteMessageGasUsage(uint256 _usage) external;
}
================================================
FILE: contracts/governed-owner/interfaces/ISgnOwner.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface ISgnOwner {
function setWhitelistEnabled(bool _whitelistEnabled) external;
function addWhitelisted(address _account) external;
function removeWhitelisted(address _account) external;
function setGovContract(address _addr) external;
function setRewardContract(address _addr) external;
function setMaxSlashFactor(uint256 _maxSlashFactor) external;
}
================================================
FILE: contracts/governed-owner/interfaces/IUpgradeableOwner.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IUpgradeableOwner {
function changeProxyAdmin(address _proxy, address _newAdmin) external;
function upgrade(address _proxy, address _implementation) external;
function upgradeAndCall(
address _proxy,
address _implementation,
bytes calldata _data
) external;
function upgradeTo(address _implementation) external;
function upgradeToAndCall(address _implementation, bytes calldata _data) external;
}
================================================
FILE: contracts/governed-owner/proxies/BridgeOwnerProxy.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "./OwnerProxyBase.sol";
import "../interfaces/IBridgeOwner.sol";
import {SimpleGovernance as sg} from "../SimpleGovernance.sol";
import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol";
abstract contract BridgeOwnerProxy is OwnerProxyBase {
// for bridges
event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers);
event NotifyResetSignersProposalCreated(uint256 proposalId, address target);
event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period);
event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token);
event UpdateSupplyProposalCreated(
uint256 proposalId,
address target,
dt.Action action,
address token,
uint256 supply
);
event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account);
// for bridge tokens
event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr);
event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap);
event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap);
function proposeResetSigners(
address _target,
address[] calldata _signers,
uint256[] calldata _powers
) external {
bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers);
}
function proposeNotifyResetSigners(address _target) external {
bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);
emit NotifyResetSignersProposalCreated(proposalId, _target);
}
function proposeIncreaseNoticePeriod(address _target, uint256 _period) external {
bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period);
}
function proposeSetNativeWrap(address _target, address _token) external {
bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit SetNativeWrapProposalCreated(proposalId, _target, _token);
}
function proposeUpdateSupply(
address _target,
dt.Action _action,
address _token,
uint256 _supply
) external {
bytes4 selector;
if (_action == dt.Action.Set) {
selector = IBridgeOwner.setSupply.selector;
} else if (_action == dt.Action.Add) {
selector = IBridgeOwner.increaseSupply.selector;
} else if (_action == dt.Action.Remove) {
selector = IBridgeOwner.decreaseSupply.selector;
} else {
revert("invalid action");
}
bytes memory data = abi.encodeWithSelector(selector, _token, _supply);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);
emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply);
}
function proposeUpdateGovernor(
address _target,
dt.Action _action,
address _account
) external {
bytes4 selector;
if (_action == dt.Action.Add) {
selector = IBridgeOwner.addGovernor.selector;
} else if (_action == dt.Action.Remove) {
selector = IBridgeOwner.removeGovernor.selector;
} else {
revert("invalid action");
}
bytes memory data = abi.encodeWithSelector(selector, _account);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);
emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account);
}
function proposeUpdateBridgeSupplyCap(
address _target,
address _bridge,
uint256 _cap
) external {
bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap);
}
function proposeSetBridgeTokenSwapCap(
address _target,
address _bridgeToken,
uint256 _swapCap
) external {
bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap);
}
}
================================================
FILE: contracts/governed-owner/proxies/CommonOwnerProxy.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "./OwnerProxyBase.sol";
import "../interfaces/ICommonOwner.sol";
import {SimpleGovernance as sg} from "../SimpleGovernance.sol";
import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol";
abstract contract CommonOwnerProxy is OwnerProxyBase {
event TransferOwnershipProposalCreated(uint256 proposalId, address target, address newOwner);
event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account);
function proposeTransferOwnership(address _target, address _newOwner) external {
bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner);
}
function proposeUpdatePauser(
address _target,
dt.Action _action,
address _account
) external {
bytes4 selector;
if (_action == dt.Action.Add) {
selector = ICommonOwner.addPauser.selector;
} else if (_action == dt.Action.Remove) {
selector = ICommonOwner.removePauser.selector;
} else {
revert("invalid action");
}
bytes memory data = abi.encodeWithSelector(selector, _account);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);
emit UpdatePauserProposalCreated(proposalId, _target, _action, _account);
}
}
================================================
FILE: contracts/governed-owner/proxies/MessageOwnerProxy.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "./OwnerProxyBase.sol";
import "../interfaces/IMessageOwner.sol";
import {SimpleGovernance as sg} from "../SimpleGovernance.sol";
import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol";
abstract contract MessageOwnerProxy is OwnerProxyBase {
event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee);
event SetBridgeAddressProposalCreated(
uint256 proposalId,
address target,
dt.BridgeType bridgeType,
address bridgeAddr
);
event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage);
function proposeSetMsgFee(
address _target,
dt.MsgFeeType _feeType,
uint256 _fee
) external {
bytes4 selector;
if (_feeType == dt.MsgFeeType.PerByte) {
selector = IMessageOwner.setFeePerByte.selector;
} else if (_feeType == dt.MsgFeeType.Base) {
selector = IMessageOwner.setFeeBase.selector;
} else {
revert("invalid fee type");
}
bytes memory data = abi.encodeWithSelector(selector, _fee);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);
emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee);
}
function proposeSetBridgeAddress(
address _target,
dt.BridgeType _bridgeType,
address _bridgeAddr
) external {
bytes4 selector;
if (_bridgeType == dt.BridgeType.Liquidity) {
selector = IMessageOwner.setLiquidityBridge.selector;
} else if (_bridgeType == dt.BridgeType.PegBridge) {
selector = IMessageOwner.setPegBridge.selector;
} else if (_bridgeType == dt.BridgeType.PegVault) {
selector = IMessageOwner.setPegVault.selector;
} else if (_bridgeType == dt.BridgeType.PegBridgeV2) {
selector = IMessageOwner.setPegBridgeV2.selector;
} else if (_bridgeType == dt.BridgeType.PegVaultV2) {
selector = IMessageOwner.setPegVaultV2.selector;
} else {
revert("invalid bridge type");
}
bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr);
}
function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external {
bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage);
}
}
================================================
FILE: contracts/governed-owner/proxies/OwnerDataTypes.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
library OwnerDataTypes {
enum Action {
Set,
Add,
Remove
}
enum MsgFeeType {
PerByte,
Base
}
enum BridgeType {
Liquidity,
PegBridge,
PegVault,
PegBridgeV2,
PegVaultV2
}
}
================================================
FILE: contracts/governed-owner/proxies/OwnerProxyBase.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
import "../SimpleGovernance.sol";
abstract contract OwnerProxyBase {
SimpleGovernance public gov;
address private initializer;
constructor(address _initializer) {
initializer = _initializer;
}
function initGov(SimpleGovernance _gov) public {
require(msg.sender == initializer, "only initializer can init");
require(address(gov) == address(0), "gov addr already set");
gov = _gov;
}
}
================================================
FILE: contracts/governed-owner/proxies/SgnOwnerProxy.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "./OwnerProxyBase.sol";
import "../interfaces/ISgnOwner.sol";
import {SimpleGovernance as sg} from "../SimpleGovernance.sol";
import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol";
abstract contract SgnOwnerProxy is OwnerProxyBase {
event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled);
event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account);
event SetGovContractProposalCreated(uint256 proposalId, address target, address addr);
event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr);
event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor);
event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount);
function proposeSetWhitelistEnable(address _target, bool _enable) external {
bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable);
}
function proposeUpdateWhitelisted(
address _target,
dt.Action _action,
address _account
) external {
bytes4 selector;
if (_action == dt.Action.Add) {
selector = ISgnOwner.addWhitelisted.selector;
} else if (_action == dt.Action.Remove) {
selector = ISgnOwner.removeWhitelisted.selector;
} else {
revert("invalid action");
}
bytes memory data = abi.encodeWithSelector(selector, _account);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);
emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account);
}
function proposeSetGovContract(address _target, address _addr) external {
bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit SetGovContractProposalCreated(proposalId, _target, _addr);
}
function proposeSetRewardContract(address _target, address _addr) external {
bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit SetRewardContractProposalCreated(proposalId, _target, _addr);
}
function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external {
bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor);
}
function proposeDrainToken(
address _target,
address _token,
uint256 _amount
) external {
bytes memory data;
if (_token == address(0)) {
data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(uint256"))), _amount);
} else {
data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(address,uint256"))), _token, _amount);
}
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit DrainTokenProposalCreated(proposalId, _target, _token, _amount);
}
}
================================================
FILE: contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "./OwnerProxyBase.sol";
import "../interfaces/IUpgradeableOwner.sol";
import {SimpleGovernance as sg} from "../SimpleGovernance.sol";
import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol";
abstract contract UpgradeableOwnerProxy is OwnerProxyBase {
event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin);
event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation);
event UpgradeAndCallProposalCreated(
uint256 proposalId,
address target,
address proxy,
address implementation,
bytes data
);
event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation);
event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data);
function proposeChangeProxyAdmin(
address _target,
address _proxy,
address _newAdmin
) external {
bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin);
}
function proposeUpgrade(
address _target,
address _proxy,
address _implementation
) external {
bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation);
}
function proposeUpgradeAndCall(
address _target,
address _proxy,
address _implementation,
bytes calldata _data
) external {
bytes memory data = abi.encodeWithSelector(
IUpgradeableOwner.upgradeAndCall.selector,
_proxy,
_implementation,
_data
);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data);
}
function proposeUpgradeTo(address _target, address _implementation) external {
bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit UpgradeToProposalCreated(proposalId, _target, _implementation);
}
function proposeUpgradeToAndCall(
address _target,
address _implementation,
bytes calldata _data
) external {
bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data);
uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);
emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data);
}
}
================================================
FILE: contracts/integration-examples/ContractAsLP.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../interfaces/IPool.sol";
import "../interfaces/IWithdrawInbox.sol";
import "../safeguard/Pauser.sol";
/**
* @title Example contract to provide liquidity to {Bridge}. Supports withdrawing liquidity via {WithdrawInbox}.
*/
contract ContractAsLP is ReentrancyGuard, Pauser {
using SafeERC20 for IERC20;
address public bridge;
address public inbox;
event Deposited(address depositor, address token, uint256 amount);
constructor(address _bridge, address _inbox) {
bridge = _bridge;
inbox = _inbox;
}
/**
* @notice Deposit tokens.
* @param _token The deposited token address.
* @param _amount The amount to deposit.
*/
function deposit(address _token, uint256 _amount) external nonReentrant whenNotPaused onlyOwner {
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
emit Deposited(msg.sender, _token, _amount);
}
/**
* @notice Add liquidity to the pool-based bridge.
* NOTE: This function DOES NOT SUPPORT fee-on-transfer / rebasing tokens.
* @param _token The address of the token.
* @param _amount The amount to add.
*/
function addLiquidity(address _token, uint256 _amount) external whenNotPaused onlyOwner {
require(IERC20(_token).balanceOf(address(this)) >= _amount, "insufficient balance");
IERC20(_token).safeIncreaseAllowance(bridge, _amount);
IPool(bridge).addLiquidity(_token, _amount);
}
/**
* @notice Withdraw liquidity from the pool-based bridge.
* NOTE: Each of your withdrawal request should have different _wdSeq.
* NOTE: Tokens to withdraw within one withdrawal request should have the same symbol.
* @param _wdSeq The unique sequence number to identify this withdrawal request.
* @param _receiver The receiver address on _toChain.
* @param _toChain The chain Id to receive the withdrawn tokens.
* @param _fromChains The chain Ids to withdraw tokens.
* @param _tokens The token to withdraw on each fromChain.
* @param _ratios The withdrawal ratios of each token.
* @param _slippages The max slippages of each token for cross-chain withdraw.
*/
function withdraw(
uint64 _wdSeq,
address _receiver,
uint64 _toChain,
uint64[] calldata _fromChains,
address[] calldata _tokens,
uint32[] calldata _ratios,
uint32[] calldata _slippages
) external whenNotPaused onlyOwner {
IWithdrawInbox(inbox).withdraw(_wdSeq, _receiver, _toChain, _fromChains, _tokens, _ratios, _slippages);
}
}
================================================
FILE: contracts/integration-examples/ContractAsSender.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../libraries/BridgeTransferLib.sol";
import "../safeguard/Pauser.sol";
/**
* @title Example contract to send cBridge transfers. Supports the liquidity pool-based {Bridge}, the {OriginalTokenVault} for pegged
* deposit and the {PeggedTokenBridge} for pegged burn. Includes handling of refunds for failed transfers.
* @notice For the bad Bridge.send/PeggedTokenBridge.deposit of native token(eg.ETH) or wrapped native token(eg.WETH),
* its refund asset depends on whether the nativeWrap of Bridge/PeggedTokenBridge is set or not AT THE MOMENT OF REFUNDING.
* If the nativeWrap is set, the refund asset would always be native token (eg.ETH), even though the original sending asset
* is wrapped native token. If the nativeWrap isn't set, the refund asset would always be wrapped native token.
*/
contract ContractAsSender is ReentrancyGuard, Pauser {
using SafeERC20 for IERC20;
mapping(BridgeTransferLib.BridgeSendType => address) public bridges;
mapping(bytes32 => address) public records;
address public nativeWrap;
uint256 public nativeTokenTransferGas = 50000;
event Deposited(address depositor, address token, uint256 amount);
event BridgeUpdated(BridgeTransferLib.BridgeSendType bridgeSendType, address bridgeAddr);
/**
* @notice Send a cross-chain transfer either via liquidity pool-based bridge or in form of mint/burn.
* @param _receiver The address of the receiver.
* @param _token The address of the token.
* @param _amount The amount of the transfer.
* @param _dstChainId The destination chain ID.
* @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.
* @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.
* Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least
* (100% - max slippage percentage) * amount or the transfer can be refunded.
* Only applicable to the {BridgeSendType.Liquidity}.
* @param _bridgeSendType The type of bridge used by this transfer. One of the {BridgeSendType} enum.
*/
function transfer(
address _receiver,
address _token,
uint256 _amount,
uint64 _dstChainId,
uint64 _nonce,
uint32 _maxSlippage, // slippage * 1M, eg. 0.5% -> 5000
BridgeTransferLib.BridgeSendType _bridgeSendType
) external nonReentrant whenNotPaused onlyOwner returns (bytes32) {
address _bridgeAddr = bridges[_bridgeSendType];
require(_bridgeAddr != address(0), "unknown bridge type");
bytes32 transferId = BridgeTransferLib.sendTransfer(
_receiver,
_token,
_amount,
_dstChainId,
_nonce,
_maxSlippage,
_bridgeSendType,
_bridgeAddr
);
require(records[transferId] == address(0), "record exists");
records[transferId] = msg.sender;
return transferId;
}
/**
* @notice Refund a failed cross-chain transfer.
* @param _request The serialized request protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
* @param _bridgeSendType The type of bridge used by this failed transfer. One of the {BridgeSendType} enum.
*/
function refund(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers,
BridgeTransferLib.BridgeSendType _bridgeSendType
) external nonReentrant whenNotPaused onlyOwner returns (bytes32) {
address _bridgeAddr = bridges[_bridgeSendType];
require(_bridgeAddr != address(0), "unknown bridge type");
BridgeTransferLib.ReceiveInfo memory refundInfo = BridgeTransferLib.receiveTransfer(
_request,
_sigs,
_signers,
_powers,
BridgeTransferLib.bridgeRefundType(_bridgeSendType),
_bridgeAddr
);
require(refundInfo.receiver == address(this), "invalid refund");
address _receiver = records[refundInfo.refid];
require(_receiver != address(0), "unknown transfer id or already refunded");
delete records[refundInfo.refid];
_sendToken(_receiver, refundInfo.token, refundInfo.amount);
return refundInfo.transferId;
}
/**
* @notice Send token to user. For native token and wrapped native token, this contract may not have enough _token to
* send to _receiver. This may caused by others refund an original transfer that is sent from this contract via cBridge
* contract right before you call refund function of this contract and then the nativeWrap of cBridge contract is
* modified right after that the refund triggered by that guy completes.
* As a consequence, native token and wrapped native token possessed by this contract are mixed. But don't worry,
* the total sum of two tokens keeps correct. So in order to avoid deadlocking any token, we'd better have a
* balance check before sending out native token or wrapped native token. If the balance of _token is not sufficient,
* we change to sent the other token.
*/
function _sendToken(
address _receiver,
address _token,
uint256 _amount
) internal {
if (_token == address(0)) {
// refund asset is ETH
if (address(this).balance >= _amount) {
(bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}("");
require(sent, "failed to send native token");
} else {
// in case of refund asset is WETH
IERC20(_token).safeTransfer(_receiver, _amount);
}
} else if (_token == nativeWrap) {
// refund asset is WETH
if (IERC20(_token).balanceOf(address(this)) >= _amount) {
IERC20(_token).safeTransfer(_receiver, _amount);
} else {
// in case of refund asset is ETH
(bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}("");
require(sent, "failed to send native token");
}
} else {
IERC20(_token).safeTransfer(_receiver, _amount);
}
}
// ----------------------Admin operation-----------------------
/**
* @notice Lock tokens.
* @param _token The deposited token address.
* @param _amount The amount to deposit.
*/
function deposit(address _token, uint256 _amount) external nonReentrant whenNotPaused onlyOwner {
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
emit Deposited(msg.sender, _token, _amount);
}
function setBridgeAddress(BridgeTransferLib.BridgeSendType _bridgeSendType, address _addr) public onlyOwner {
require(_addr != address(0), "invalid address");
bridges[_bridgeSendType] = _addr;
emit BridgeUpdated(_bridgeSendType, _addr);
}
// set nativeWrap
function setWrap(address _weth) external onlyOwner {
nativeWrap = _weth;
}
// setNativeTransferGasUsed, native transfer will use this config.
function setNativeTokenTransferGas(uint256 _gasUsed) external onlyOwner {
nativeTokenTransferGas = _gasUsed;
}
// This is needed to receive ETH if a refund asset is ETH
receive() external payable {}
}
================================================
FILE: contracts/interfaces/IBridge.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IBridge {
function send(
address _receiver,
address _token,
uint256 _amount,
uint64 _dstChainId,
uint64 _nonce,
uint32 _maxSlippage
) external;
function sendNative(
address _receiver,
uint256 _amount,
uint64 _dstChainId,
uint64 _nonce,
uint32 _maxSlippage
) external payable;
function relay(
bytes calldata _relayRequest,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external;
function transfers(bytes32 transferId) external view returns (bool);
function withdraws(bytes32 withdrawId) external view returns (bool);
function withdraw(
bytes calldata _wdmsg,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external;
/**
* @notice Verifies that a message is signed by a quorum among the signers.
* @param _msg signed message
* @param _sigs list of signatures sorted by signer addresses in ascending order
* @param _signers sorted list of current signers
* @param _powers powers of current signers
*/
function verifySigs(
bytes memory _msg,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external view;
}
================================================
FILE: contracts/interfaces/ICircleBridge.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface ICircleBridge {
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain.
* Emits a `DepositForBurn` event.
* @dev reverts if:
* - given burnToken is not supported
* - given destinationDomain has no CircleBridge registered
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
* to this contract is less than `amount`.
* - burn() reverts. For example, if `amount` is 0.
* - MessageTransmitter returns false or reverts.
* @param _amount amount of tokens to burn
* @param _destinationDomain destination domain (ETH = 0, AVAX = 1)
* @param _mintRecipient address of mint recipient on destination domain
* @param _burnToken address of contract to burn deposited tokens, on local domain
* @return _nonce unique nonce reserved by message
*/
function depositForBurn(
uint256 _amount,
uint32 _destinationDomain,
bytes32 _mintRecipient,
address _burnToken
) external returns (uint64 _nonce);
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain.
* Emits a `DepositForBurn` event.
* @dev reverts if:
* - given burnToken is not supported
* - given destinationDomain has no TokenMessenger registered
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
* to this contract is less than `amount`.
* - burn() reverts. For example, if `amount` is 0.
* - maxFee is greater than or equal to `amount`.
* - MessageTransmitterV2#sendMessage reverts.
* @param amount amount of tokens to burn
* @param destinationDomain destination domain to receive message on
* @param mintRecipient address of mint recipient on destination domain
* @param burnToken token to burn `amount` of, on local domain
* @param destinationCaller authorized caller on the destination domain, as bytes32. If equal to bytes32(0),
* any address can broadcast the message.
* @param maxFee maximum fee to pay on the destination domain, specified in units of burnToken
* @param minFinalityThreshold the minimum finality at which a burn message will be attested to.
*/
function depositForBurn(
uint256 amount,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken,
bytes32 destinationCaller,
uint256 maxFee,
uint32 minFinalityThreshold
) external;
}
================================================
FILE: contracts/interfaces/IDelayedTransfer.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.9;
interface IDelayedTransfer {
struct delayedTransfer {
address receiver;
address token;
uint256 amount;
uint256 timestamp;
}
function delayedTransfers(bytes32 transferId) external view returns (delayedTransfer memory);
}
================================================
FILE: contracts/interfaces/IOriginalTokenVault.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IOriginalTokenVault {
/**
* @notice Lock original tokens to trigger mint at a remote chain's PeggedTokenBridge
* @param _token local token address
* @param _amount locked token amount
* @param _mintChainId destination chainId to mint tokens
* @param _mintAccount destination account to receive minted tokens
* @param _nonce user input to guarantee unique depositId
*/
function deposit(
address _token,
uint256 _amount,
uint64 _mintChainId,
address _mintAccount,
uint64 _nonce
) external;
/**
* @notice Lock native token as original token to trigger mint at a remote chain's PeggedTokenBridge
* @param _amount locked token amount
* @param _mintChainId destination chainId to mint tokens
* @param _mintAccount destination account to receive minted tokens
* @param _nonce user input to guarantee unique depositId
*/
function depositNative(
uint256 _amount,
uint64 _mintChainId,
address _mintAccount,
uint64 _nonce
) external payable;
/**
* @notice Withdraw locked original tokens triggered by a burn at a remote chain's PeggedTokenBridge.
* @param _request The serialized Withdraw protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by
* +2/3 of the bridge's current signing power to be delivered.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
*/
function withdraw(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external;
function records(bytes32 recordId) external view returns (bool);
}
================================================
FILE: contracts/interfaces/IOriginalTokenVaultV2.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IOriginalTokenVaultV2 {
/**
* @notice Lock original tokens to trigger mint at a remote chain's PeggedTokenBridge
* @param _token local token address
* @param _amount locked token amount
* @param _mintChainId destination chainId to mint tokens
* @param _mintAccount destination account to receive minted tokens
* @param _nonce user input to guarantee unique depositId
*/
function deposit(
address _token,
uint256 _amount,
uint64 _mintChainId,
address _mintAccount,
uint64 _nonce
) external returns (bytes32);
/**
* @notice Lock native token as original token to trigger mint at a remote chain's PeggedTokenBridge
* @param _amount locked token amount
* @param _mintChainId destination chainId to mint tokens
* @param _mintAccount destination account to receive minted tokens
* @param _nonce user input to guarantee unique depositId
*/
function depositNative(
uint256 _amount,
uint64 _mintChainId,
address _mintAccount,
uint64 _nonce
) external payable returns (bytes32);
/**
* @notice Withdraw locked original tokens triggered by a burn at a remote chain's PeggedTokenBridge.
* @param _request The serialized Withdraw protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by
* +2/3 of the bridge's current signing power to be delivered.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
*/
function withdraw(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external returns (bytes32);
function records(bytes32 recordId) external view returns (bool);
}
================================================
FILE: contracts/interfaces/IPeggedToken.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IPeggedToken {
function mint(address _to, uint256 _amount) external;
function burn(address _from, uint256 _amount) external;
}
================================================
FILE: contracts/interfaces/IPeggedTokenBridge.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IPeggedTokenBridge {
/**
* @notice Burn tokens to trigger withdrawal at a remote chain's OriginalTokenVault
* @param _token local token address
* @param _amount locked token amount
* @param _withdrawAccount account who withdraw original tokens on the remote chain
* @param _nonce user input to guarantee unique depositId
*/
function burn(
address _token,
uint256 _amount,
address _withdrawAccount,
uint64 _nonce
) external;
/**
* @notice Mint tokens triggered by deposit at a remote chain's OriginalTokenVault.
* @param _request The serialized Mint protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by
* +2/3 of the sigsVerifier's current signing power to be delivered.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
*/
function mint(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external;
function records(bytes32 recordId) external view returns (bool);
}
================================================
FILE: contracts/interfaces/IPeggedTokenBridgeV2.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IPeggedTokenBridgeV2 {
/**
* @notice Burn pegged tokens to trigger a cross-chain withdrawal of the original tokens at a remote chain's
* OriginalTokenVault, or mint at another remote chain
* @param _token The pegged token address.
* @param _amount The amount to burn.
* @param _toChainId If zero, withdraw from original vault; otherwise, the remote chain to mint tokens.
* @param _toAccount The account to receive tokens on the remote chain
* @param _nonce A number to guarantee unique depositId. Can be timestamp in practice.
*/
function burn(
address _token,
uint256 _amount,
uint64 _toChainId,
address _toAccount,
uint64 _nonce
) external returns (bytes32);
// same with `burn` above, use openzeppelin ERC20Burnable interface
function burnFrom(
address _token,
uint256 _amount,
uint64 _toChainId,
address _toAccount,
uint64 _nonce
) external returns (bytes32);
/**
* @notice Mint tokens triggered by deposit at a remote chain's OriginalTokenVault.
* @param _request The serialized Mint protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by
* +2/3 of the sigsVerifier's current signing power to be delivered.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
*/
function mint(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external returns (bytes32);
function records(bytes32 recordId) external view returns (bool);
}
================================================
FILE: contracts/interfaces/IPeggedTokenBurnFrom.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
// used for pegged token with openzeppelin ERC20Burnable interface
// only compatible with PeggedTokenBridgeV2
interface IPeggedTokenBurnFrom {
function mint(address _to, uint256 _amount) external;
function burnFrom(address _from, uint256 _amount) external;
}
================================================
FILE: contracts/interfaces/IPool.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IPool {
function addLiquidity(address _token, uint256 _amount) external;
function withdraws(bytes32 withdrawId) external view returns (bool);
function withdraw(
bytes calldata _wdmsg,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external;
}
================================================
FILE: contracts/interfaces/ISigsVerifier.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface ISigsVerifier {
/**
* @notice Verifies that a message is signed by a quorum among the signers.
* @param _msg signed message
* @param _sigs list of signatures sorted by signer addresses in ascending order
* @param _signers sorted list of current signers
* @param _powers powers of current signers
*/
function verifySigs(
bytes memory _msg,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external view;
}
================================================
FILE: contracts/interfaces/IUniswapV2.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IUniswapV2 {
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
}
================================================
FILE: contracts/interfaces/IWETH.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IWETH {
function deposit() external payable;
function withdraw(uint256) external;
}
================================================
FILE: contracts/interfaces/IWithdrawInbox.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
interface IWithdrawInbox {
function withdraw(
uint64 _wdSeq,
address _receiver,
uint64 _toChain,
uint64[] calldata _fromChains,
address[] calldata _tokens,
uint32[] calldata _ratios,
uint32[] calldata _slippages
) external;
}
================================================
FILE: contracts/libraries/BridgeTransferLib.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./PbBridge.sol";
import "./PbPegged.sol";
import "./PbPool.sol";
import "../interfaces/IBridge.sol";
import "../interfaces/IOriginalTokenVault.sol";
import "../interfaces/IOriginalTokenVaultV2.sol";
import "../interfaces/IPeggedTokenBridge.sol";
import "../interfaces/IPeggedTokenBridgeV2.sol";
interface INativeWrap {
function nativeWrap() external view returns (address);
}
library BridgeTransferLib {
using SafeERC20 for IERC20;
enum BridgeSendType {
Null,
Liquidity,
PegDeposit,
PegBurn,
PegV2Deposit,
PegV2Burn,
PegV2BurnFrom
}
enum BridgeReceiveType {
Null,
LqRelay,
LqWithdraw,
PegMint,
PegWithdraw,
PegV2Mint,
PegV2Withdraw
}
struct ReceiveInfo {
bytes32 transferId;
address receiver;
address token; // 0 address for native token
uint256 amount;
bytes32 refid; // reference id, e.g., srcTransferId for refund
}
// ============== Internal library functions called by apps ==============
/**
* @notice Send a cross-chain transfer of ERC20 token either via liquidity pool-based bridge or in the form of pegged mint / burn.
* @param _receiver The address of the receiver.
* @param _token The address of the token.
* @param _amount The amount of the transfer.
* @param _dstChainId The destination chain ID.
* @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.
* @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.
* Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least
* (100% - max slippage percentage) * amount or the transfer can be refunded.
* Only applicable to the {BridgeSendType.Liquidity}.
* @param _bridgeSendType The type of the bridge used by this transfer. One of the {BridgeSendType} enum.
* @param _bridgeAddr The address of the bridge used.
*/
function sendTransfer(
address _receiver,
address _token,
uint256 _amount,
uint64 _dstChainId,
uint64 _nonce,
uint32 _maxSlippage, // slippage * 1M, eg. 0.5% -> 5000
BridgeSendType _bridgeSendType,
address _bridgeAddr
) internal returns (bytes32) {
bytes32 transferId;
IERC20(_token).safeIncreaseAllowance(_bridgeAddr, _amount);
if (_bridgeSendType == BridgeSendType.Liquidity) {
IBridge(_bridgeAddr).send(_receiver, _token, _amount, _dstChainId, _nonce, _maxSlippage);
transferId = keccak256(
abi.encodePacked(address(this), _receiver, _token, _amount, _dstChainId, _nonce, uint64(block.chainid))
);
} else if (_bridgeSendType == BridgeSendType.PegDeposit) {
IOriginalTokenVault(_bridgeAddr).deposit(_token, _amount, _dstChainId, _receiver, _nonce);
transferId = keccak256(
abi.encodePacked(address(this), _token, _amount, _dstChainId, _receiver, _nonce, uint64(block.chainid))
);
} else if (_bridgeSendType == BridgeSendType.PegBurn) {
IPeggedTokenBridge(_bridgeAddr).burn(_token, _amount, _receiver, _nonce);
transferId = keccak256(
abi.encodePacked(address(this), _token, _amount, _receiver, _nonce, uint64(block.chainid))
);
// handle cases where certain tokens do not spend allowance for role-based burn
IERC20(_token).safeApprove(_bridgeAddr, 0);
} else if (_bridgeSendType == BridgeSendType.PegV2Deposit) {
transferId = IOriginalTokenVaultV2(_bridgeAddr).deposit(_token, _amount, _dstChainId, _receiver, _nonce);
} else if (_bridgeSendType == BridgeSendType.PegV2Burn) {
transferId = IPeggedTokenBridgeV2(_bridgeAddr).burn(_token, _amount, _dstChainId, _receiver, _nonce);
// handle cases where certain tokens do not spend allowance for role-based burn
IERC20(_token).safeApprove(_bridgeAddr, 0);
} else if (_bridgeSendType == BridgeSendType.PegV2BurnFrom) {
transferId = IPeggedTokenBridgeV2(_bridgeAddr).burnFrom(_token, _amount, _dstChainId, _receiver, _nonce);
// handle cases where certain tokens do not spend allowance for role-based burn
IERC20(_token).safeApprove(_bridgeAddr, 0);
} else {
revert("bridge send type not supported");
}
return transferId;
}
/**
* @notice Send a cross-chain transfer of native token either via liquidity pool-based bridge or in the form of pegged mint / burn.
* @param _receiver The address of the receiver.
* @param _amount The amount of the transfer.
* @param _dstChainId The destination chain ID.
* @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.
* @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.
* Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least
* (100% - max slippage percentage) * amount or the transfer can be refunded.
* Only applicable to the {BridgeSendType.Liquidity}.
* @param _bridgeSendType The type of the bridge used by this transfer. One of the {BridgeSendType} enum.
* @param _bridgeAddr The address of the bridge used.
*/
function sendNativeTransfer(
address _receiver,
uint256 _amount,
uint64 _dstChainId,
uint64 _nonce,
uint32 _maxSlippage, // slippage * 1M, eg. 0.5% -> 5000
BridgeSendType _bridgeSendType,
address _bridgeAddr
) internal returns (bytes32) {
require(
_bridgeSendType == BridgeSendType.Liquidity ||
_bridgeSendType == BridgeSendType.PegDeposit ||
_bridgeSendType == BridgeSendType.PegV2Deposit,
"Lib: invalid bridge send type"
);
address _token = INativeWrap(_bridgeAddr).nativeWrap();
bytes32 transferId;
if (_bridgeSendType == BridgeSendType.Liquidity) {
IBridge(_bridgeAddr).sendNative{value: msg.value}(_receiver, _amount, _dstChainId, _nonce, _maxSlippage);
transferId = keccak256(
abi.encodePacked(address(this), _receiver, _token, _amount, _dstChainId, _nonce, uint64(block.chainid))
);
} else if (_bridgeSendType == BridgeSendType.PegDeposit) {
IOriginalTokenVault(_bridgeAddr).depositNative{value: msg.value}(_amount, _dstChainId, _receiver, _nonce);
transferId = keccak256(
abi.encodePacked(address(this), _token, _amount, _dstChainId, _receiver, _nonce, uint64(block.chainid))
);
} else {
// _bridgeSendType == BridgeSendType.PegV2Deposit
transferId = IOriginalTokenVaultV2(_bridgeAddr).depositNative{value: msg.value}(
_amount,
_dstChainId,
_receiver,
_nonce
);
}
return transferId;
}
/**
* @notice Receive a cross-chain transfer.
* @param _request The serialized request protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
* @param _bridgeReceiveType The type of the received transfer. One of the {BridgeReceiveType} enum.
* @param _bridgeAddr The address of the bridge used.
*/
function receiveTransfer(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers,
BridgeReceiveType _bridgeReceiveType,
address _bridgeAddr
) internal returns (ReceiveInfo memory) {
if (_bridgeReceiveType == BridgeReceiveType.LqRelay) {
return receiveLiquidityRelay(_request, _sigs, _signers, _powers, _bridgeAddr);
} else if (_bridgeReceiveType == BridgeReceiveType.LqWithdraw) {
return receiveLiquidityWithdraw(_request, _sigs, _signers, _powers, _bridgeAddr);
} else if (_bridgeReceiveType == BridgeReceiveType.PegWithdraw) {
return receivePegWithdraw(_request, _sigs, _signers, _powers, _bridgeAddr);
} else if (_bridgeReceiveType == BridgeReceiveType.PegMint) {
return receivePegMint(_request, _sigs, _signers, _powers, _bridgeAddr);
} else if (_bridgeReceiveType == BridgeReceiveType.PegV2Withdraw) {
return receivePegV2Withdraw(_request, _sigs, _signers, _powers, _bridgeAddr);
} else if (_bridgeReceiveType == BridgeReceiveType.PegV2Mint) {
return receivePegV2Mint(_request, _sigs, _signers, _powers, _bridgeAddr);
} else {
revert("bridge receive type not supported");
}
}
/**
* @notice Receive a liquidity bridge relay.
* @param _request The serialized request protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
* @param _bridgeAddr The address of liquidity bridge.
*/
function receiveLiquidityRelay(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers,
address _bridgeAddr
) internal returns (ReceiveInfo memory) {
ReceiveInfo memory recv;
PbBridge.Relay memory request = PbBridge.decRelay(_request);
recv.transferId = keccak256(
abi.encodePacked(
request.sender,
request.receiver,
request.token,
request.amount,
request.srcChainId,
uint64(block.chainid),
request.srcTransferId
)
);
recv.refid = request.srcTransferId;
recv.receiver = request.receiver;
recv.token = request.token;
recv.amount = request.amount;
if (!IBridge(_bridgeAddr).transfers(recv.transferId)) {
IBridge(_bridgeAddr).relay(_request, _sigs, _signers, _powers);
}
return recv;
}
/**
* @notice Receive a liquidity bridge withdrawal.
* @param _request The serialized request protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
* @param _bridgeAddr The address of liquidity bridge.
*/
function receiveLiquidityWithdraw(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers,
address _bridgeAddr
) internal returns (ReceiveInfo memory) {
ReceiveInfo memory recv;
PbPool.WithdrawMsg memory request = PbPool.decWithdrawMsg(_request);
recv.transferId = keccak256(
abi.encodePacked(request.chainid, request.seqnum, request.receiver, request.token, request.amount)
);
recv.refid = request.refid;
recv.receiver = request.receiver;
if (INativeWrap(_bridgeAddr).nativeWrap() == request.token) {
recv.token = address(0);
} else {
recv.token = request.token;
}
recv.amount = request.amount;
if (!IBridge(_bridgeAddr).withdraws(recv.transferId)) {
IBridge(_bridgeAddr).withdraw(_request, _sigs, _signers, _powers);
}
return recv;
}
/**
* @notice Receive an OriginalTokenVault withdrawal.
* @param _request The serialized request protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
* @param _bridgeAddr The address of OriginalTokenVault.
*/
function receivePegWithdraw(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers,
address _bridgeAddr
) internal returns (ReceiveInfo memory) {
ReceiveInfo memory recv;
PbPegged.Withdraw memory request = PbPegged.decWithdraw(_request);
recv.transferId = keccak256(
abi.encodePacked(
request.receiver,
request.token,
request.amount,
request.burnAccount,
request.refChainId,
request.refId
)
);
recv.refid = request.refId;
recv.receiver = request.receiver;
if (INativeWrap(_bridgeAddr).nativeWrap() == request.token) {
recv.token = address(0);
} else {
recv.token = request.token;
}
recv.amount = request.amount;
if (!IOriginalTokenVault(_bridgeAddr).records(recv.transferId)) {
IOriginalTokenVault(_bridgeAddr).withdraw(_request, _sigs, _signers, _powers);
}
return recv;
}
/**
* @notice Receive a PeggedTokenBridge mint.
* @param _request The serialized request protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
* @param _bridgeAddr The address of PeggedTokenBridge.
*/
function receivePegMint(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers,
address _bridgeAddr
) internal returns (ReceiveInfo memory) {
ReceiveInfo memory recv;
PbPegged.Mint memory request = PbPegged.decMint(_request);
recv.transferId = keccak256(
abi.encodePacked(
request.account,
request.token,
request.amount,
request.depositor,
request.refChainId,
request.refId
)
);
recv.refid = request.refId;
recv.receiver = request.account;
recv.token = request.token;
recv.amount = request.amount;
if (!IPeggedTokenBridge(_bridgeAddr).records(recv.transferId)) {
IPeggedTokenBridge(_bridgeAddr).mint(_request, _sigs, _signers, _powers);
}
return recv;
}
/**
* @notice Receive an OriginalTokenVaultV2 withdrawal.
* @param _request The serialized request protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A request must be signed-off by
* +2/3 of the bridge's current signing power to be delivered.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
* @param _bridgeAddr The address of OriginalTokenVaultV2.
*/
function receivePegV2Withdraw(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers,
address _bridgeAddr
) internal returns (ReceiveInfo memory) {
ReceiveInfo memory recv;
PbPegged.Withdraw memory request = PbPegged.decWithdraw(_request);
if (IOriginalTokenVaultV2(_bridgeAddr).records(request.refId)) {
recv.transferId = keccak256(
abi.encodePacked(
request.receiver,
request.token,
request.amount,
request.burnAccount,
request.refChainId,
request.refId,
_bridgeAddr
)
);
} else {
recv.transferId = IOriginalTokenVaultV2(_bridgeAddr).withdraw(_request, _sigs, _signers, _powers);
}
recv.refid = request.refId;
recv.receiver = request.receiver;
if (INativeWrap(_bridgeAddr).nativeWrap() == request.token) {
recv.token = address(0);
} else {
recv.token = request.token;
}
recv.amount = request.amount;
return recv;
}
/**
* @notice Receive a PeggedTokenBridgeV2 mint.
* @param _request The serialized request protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A request must be signed-off by
* +2/3 of the bridge's current signing power to be delivered.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
* @param _bridgeAddr The address of PeggedTokenBridgeV2.
*/
function receivePegV2Mint(
bytes calldata _request,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers,
address _bridgeAddr
) internal returns (ReceiveInfo memory) {
ReceiveInfo memory recv;
PbPegged.Mint memory request = PbPegged.decMint(_request);
if (IPeggedTokenBridgeV2(_bridgeAddr).records(request.refId)) {
recv.transferId = keccak256(
abi.encodePacked(
request.account,
request.token,
request.amount,
request.depositor,
request.refChainId,
request.refId,
_bridgeAddr
)
);
} else {
recv.transferId = IPeggedTokenBridgeV2(_bridgeAddr).mint(_request, _sigs, _signers, _powers);
}
recv.refid = request.refId;
recv.receiver = request.account;
recv.token = request.token;
recv.amount = request.amount;
return recv;
}
function bridgeRefundType(BridgeSendType _bridgeSendType) internal pure returns (BridgeReceiveType) {
if (_bridgeSendType == BridgeSendType.Liquidity) {
return BridgeReceiveType.LqWithdraw;
}
if (_bridgeSendType == BridgeSendType.PegDeposit) {
return BridgeReceiveType.PegWithdraw;
}
if (_bridgeSendType == BridgeSendType.PegBurn) {
return BridgeReceiveType.PegMint;
}
if (_bridgeSendType == BridgeSendType.PegV2Deposit) {
return BridgeReceiveType.PegV2Withdraw;
}
if (_bridgeSendType == BridgeSendType.PegV2Burn || _bridgeSendType == BridgeSendType.PegV2BurnFrom) {
return BridgeReceiveType.PegV2Mint;
}
return BridgeReceiveType.Null;
}
}
================================================
FILE: contracts/libraries/Pb.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
// runtime proto sol library
library Pb {
enum WireType {
Varint,
Fixed64,
LengthDelim,
StartGroup,
EndGroup,
Fixed32
}
struct Buffer {
uint256 idx; // the start index of next read. when idx=b.length, we're done
bytes b; // hold serialized proto msg, readonly
}
// create a new in-memory Buffer object from raw msg bytes
function fromBytes(bytes memory raw) internal pure returns (Buffer memory buf) {
buf.b = raw;
buf.idx = 0;
}
// whether there are unread bytes
function hasMore(Buffer memory buf) internal pure returns (bool) {
return buf.idx < buf.b.length;
}
// decode current field number and wiretype
function decKey(Buffer memory buf) internal pure returns (uint256 tag, WireType wiretype) {
uint256 v = decVarint(buf);
tag = v / 8;
wiretype = WireType(v & 7);
}
// count tag occurrences, return an array due to no memory map support
// have to create array for (maxtag+1) size. cnts[tag] = occurrences
// should keep buf.idx unchanged because this is only a count function
function cntTags(Buffer memory buf, uint256 maxtag) internal pure returns (uint256[] memory cnts) {
uint256 originalIdx = buf.idx;
cnts = new uint256[](maxtag + 1); // protobuf's tags are from 1 rather than 0
uint256 tag;
WireType wire;
while (hasMore(buf)) {
(tag, wire) = decKey(buf);
cnts[tag] += 1;
skipValue(buf, wire);
}
buf.idx = originalIdx;
}
// read varint from current buf idx, move buf.idx to next read, return the int value
function decVarint(Buffer memory buf) internal pure returns (uint256 v) {
bytes10 tmp; // proto int is at most 10 bytes (7 bits can be used per byte)
bytes memory bb = buf.b; // get buf.b mem addr to use in assembly
v = buf.idx; // use v to save one additional uint variable
assembly {
tmp := mload(add(add(bb, 32), v)) // load 10 bytes from buf.b[buf.idx] to tmp
}
uint256 b; // store current byte content
v = 0; // reset to 0 for return value
for (uint256 i = 0; i < 10; i++) {
assembly {
b := byte(i, tmp) // don't use tmp[i] because it does bound check and costs extra
}
v |= (b & 0x7F) << (i * 7);
if (b & 0x80 == 0) {
buf.idx += i + 1;
return v;
}
}
revert(); // i=10, invalid varint stream
}
// read length delimited field and return bytes
function decBytes(Buffer memory buf) internal pure returns (bytes memory b) {
uint256 len = decVarint(buf);
uint256 end = buf.idx + len;
require(end <= buf.b.length); // avoid overflow
b = new bytes(len);
bytes memory bufB = buf.b; // get buf.b mem addr to use in assembly
uint256 bStart;
uint256 bufBStart = buf.idx;
assembly {
bStart := add(b, 32)
bufBStart := add(add(bufB, 32), bufBStart)
}
for (uint256 i = 0; i < len; i += 32) {
assembly {
mstore(add(bStart, i), mload(add(bufBStart, i)))
}
}
buf.idx = end;
}
// return packed ints
function decPacked(Buffer memory buf) internal pure returns (uint256[] memory t) {
uint256 len = decVarint(buf);
uint256 end = buf.idx + len;
require(end <= buf.b.length); // avoid overflow
// array in memory must be init w/ known length
// so we have to create a tmp array w/ max possible len first
uint256[] memory tmp = new uint256[](len);
uint256 i = 0; // count how many ints are there
while (buf.idx < end) {
tmp[i] = decVarint(buf);
i++;
}
t = new uint256[](i); // init t with correct length
for (uint256 j = 0; j < i; j++) {
t[j] = tmp[j];
}
return t;
}
// move idx pass current value field, to beginning of next tag or msg end
function skipValue(Buffer memory buf, WireType wire) internal pure {
if (wire == WireType.Varint) {
decVarint(buf);
} else if (wire == WireType.LengthDelim) {
uint256 len = decVarint(buf);
buf.idx += len; // skip len bytes value data
require(buf.idx <= buf.b.length); // avoid overflow
} else {
revert();
} // unsupported wiretype
}
// type conversion help utils
function _bool(uint256 x) internal pure returns (bool v) {
return x != 0;
}
function _uint256(bytes memory b) internal pure returns (uint256 v) {
require(b.length <= 32); // b's length must be smaller than or equal to 32
assembly {
v := mload(add(b, 32))
} // load all 32bytes to v
v = v >> (8 * (32 - b.length)); // only first b.length is valid
}
function _address(bytes memory b) internal pure returns (address v) {
v = _addressPayable(b);
}
function _addressPayable(bytes memory b) internal pure returns (address payable v) {
require(b.length == 20);
//load 32bytes then shift right 12 bytes
assembly {
v := div(mload(add(b, 32)), 0x1000000000000000000000000)
}
}
function _bytes32(bytes memory b) internal pure returns (bytes32 v) {
require(b.length == 32);
assembly {
v := mload(add(b, 32))
}
}
// uint[] to uint8[]
function uint8s(uint256[] memory arr) internal pure returns (uint8[] memory t) {
t = new uint8[](arr.length);
for (uint256 i = 0; i < t.length; i++) {
t[i] = uint8(arr[i]);
}
}
function uint32s(uint256[] memory arr) internal pure returns (uint32[] memory t) {
t = new uint32[](arr.length);
for (uint256 i = 0; i < t.length; i++) {
t[i] = uint32(arr[i]);
}
}
function uint64s(uint256[] memory arr) internal pure returns (uint64[] memory t) {
t = new uint64[](arr.length);
for (uint256 i = 0; i < t.length; i++) {
t[i] = uint64(arr[i]);
}
}
function bools(uint256[] memory arr) internal pure returns (bool[] memory t) {
t = new bool[](arr.length);
for (uint256 i = 0; i < t.length; i++) {
t[i] = arr[i] != 0;
}
}
}
================================================
FILE: contracts/libraries/PbBridge.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
// Code generated by protoc-gen-sol. DO NOT EDIT.
// source: bridge.proto
pragma solidity 0.8.17;
import "./Pb.sol";
library PbBridge {
using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj
struct Relay {
address sender; // tag: 1
address receiver; // tag: 2
address token; // tag: 3
uint256 amount; // tag: 4
uint64 srcChainId; // tag: 5
uint64 dstChainId; // tag: 6
bytes32 srcTransferId; // tag: 7
} // end struct Relay
function decRelay(bytes memory raw) internal pure returns (Relay memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.sender = Pb._address(buf.decBytes());
} else if (tag == 2) {
m.receiver = Pb._address(buf.decBytes());
} else if (tag == 3) {
m.token = Pb._address(buf.decBytes());
} else if (tag == 4) {
m.amount = Pb._uint256(buf.decBytes());
} else if (tag == 5) {
m.srcChainId = uint64(buf.decVarint());
} else if (tag == 6) {
m.dstChainId = uint64(buf.decVarint());
} else if (tag == 7) {
m.srcTransferId = Pb._bytes32(buf.decBytes());
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder Relay
}
================================================
FILE: contracts/libraries/PbFarming.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
// Code generated by protoc-gen-sol. DO NOT EDIT.
// source: contracts/libraries/proto/farming.proto
pragma solidity 0.8.17;
import "./Pb.sol";
library PbFarming {
using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj
struct FarmingRewards {
address recipient; // tag: 1
address[] tokenAddresses; // tag: 2
uint256[] cumulativeRewardAmounts; // tag: 3
} // end struct FarmingRewards
function decFarmingRewards(bytes memory raw) internal pure returns (FarmingRewards memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256[] memory cnts = buf.cntTags(3);
m.tokenAddresses = new address[](cnts[2]);
cnts[2] = 0; // reset counter for later use
m.cumulativeRewardAmounts = new uint256[](cnts[3]);
cnts[3] = 0; // reset counter for later use
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.recipient = Pb._address(buf.decBytes());
} else if (tag == 2) {
m.tokenAddresses[cnts[2]] = Pb._address(buf.decBytes());
cnts[2]++;
} else if (tag == 3) {
m.cumulativeRewardAmounts[cnts[3]] = Pb._uint256(buf.decBytes());
cnts[3]++;
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder FarmingRewards
}
================================================
FILE: contracts/libraries/PbPegged.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
// Code generated by protoc-gen-sol. DO NOT EDIT.
// source: contracts/libraries/proto/pegged.proto
pragma solidity 0.8.17;
import "./Pb.sol";
library PbPegged {
using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj
struct Mint {
address token; // tag: 1
address account; // tag: 2
uint256 amount; // tag: 3
address depositor; // tag: 4
uint64 refChainId; // tag: 5
bytes32 refId; // tag: 6
} // end struct Mint
function decMint(bytes memory raw) internal pure returns (Mint memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.token = Pb._address(buf.decBytes());
} else if (tag == 2) {
m.account = Pb._address(buf.decBytes());
} else if (tag == 3) {
m.amount = Pb._uint256(buf.decBytes());
} else if (tag == 4) {
m.depositor = Pb._address(buf.decBytes());
} else if (tag == 5) {
m.refChainId = uint64(buf.decVarint());
} else if (tag == 6) {
m.refId = Pb._bytes32(buf.decBytes());
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder Mint
struct Withdraw {
address token; // tag: 1
address receiver; // tag: 2
uint256 amount; // tag: 3
address burnAccount; // tag: 4
uint64 refChainId; // tag: 5
bytes32 refId; // tag: 6
} // end struct Withdraw
function decWithdraw(bytes memory raw) internal pure returns (Withdraw memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.token = Pb._address(buf.decBytes());
} else if (tag == 2) {
m.receiver = Pb._address(buf.decBytes());
} else if (tag == 3) {
m.amount = Pb._uint256(buf.decBytes());
} else if (tag == 4) {
m.burnAccount = Pb._address(buf.decBytes());
} else if (tag == 5) {
m.refChainId = uint64(buf.decVarint());
} else if (tag == 6) {
m.refId = Pb._bytes32(buf.decBytes());
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder Withdraw
}
================================================
FILE: contracts/libraries/PbPool.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
// Code generated by protoc-gen-sol. DO NOT EDIT.
// source: contracts/libraries/proto/pool.proto
pragma solidity 0.8.17;
import "./Pb.sol";
library PbPool {
using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj
struct WithdrawMsg {
uint64 chainid; // tag: 1
uint64 seqnum; // tag: 2
address receiver; // tag: 3
address token; // tag: 4
uint256 amount; // tag: 5
bytes32 refid; // tag: 6
} // end struct WithdrawMsg
function decWithdrawMsg(bytes memory raw) internal pure returns (WithdrawMsg memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.chainid = uint64(buf.decVarint());
} else if (tag == 2) {
m.seqnum = uint64(buf.decVarint());
} else if (tag == 3) {
m.receiver = Pb._address(buf.decBytes());
} else if (tag == 4) {
m.token = Pb._address(buf.decBytes());
} else if (tag == 5) {
m.amount = Pb._uint256(buf.decBytes());
} else if (tag == 6) {
m.refid = Pb._bytes32(buf.decBytes());
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder WithdrawMsg
}
================================================
FILE: contracts/libraries/PbSgn.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
// Code generated by protoc-gen-sol. DO NOT EDIT.
// source: contracts/libraries/proto/sgn.proto
pragma solidity 0.8.17;
import "./Pb.sol";
library PbSgn {
using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj
struct Withdrawal {
address account; // tag: 1
address token; // tag: 2
uint256 cumulativeAmount; // tag: 3
} // end struct Withdrawal
function decWithdrawal(bytes memory raw) internal pure returns (Withdrawal memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.account = Pb._address(buf.decBytes());
} else if (tag == 2) {
m.token = Pb._address(buf.decBytes());
} else if (tag == 3) {
m.cumulativeAmount = Pb._uint256(buf.decBytes());
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder Withdrawal
}
================================================
FILE: contracts/libraries/PbStaking.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
// Code generated by protoc-gen-sol. DO NOT EDIT.
// source: contracts/libraries/proto/staking.proto
pragma solidity 0.8.17;
import "./Pb.sol";
library PbStaking {
using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj
struct StakingReward {
address recipient; // tag: 1
uint256 cumulativeRewardAmount; // tag: 2
} // end struct StakingReward
function decStakingReward(bytes memory raw) internal pure returns (StakingReward memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.recipient = Pb._address(buf.decBytes());
} else if (tag == 2) {
m.cumulativeRewardAmount = Pb._uint256(buf.decBytes());
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder StakingReward
struct Slash {
address validator; // tag: 1
uint64 nonce; // tag: 2
uint64 slashFactor; // tag: 3
uint64 expireTime; // tag: 4
uint64 jailPeriod; // tag: 5
AcctAmtPair[] collectors; // tag: 6
} // end struct Slash
function decSlash(bytes memory raw) internal pure returns (Slash memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256[] memory cnts = buf.cntTags(6);
m.collectors = new AcctAmtPair[](cnts[6]);
cnts[6] = 0; // reset counter for later use
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.validator = Pb._address(buf.decBytes());
} else if (tag == 2) {
m.nonce = uint64(buf.decVarint());
} else if (tag == 3) {
m.slashFactor = uint64(buf.decVarint());
} else if (tag == 4) {
m.expireTime = uint64(buf.decVarint());
} else if (tag == 5) {
m.jailPeriod = uint64(buf.decVarint());
} else if (tag == 6) {
m.collectors[cnts[6]] = decAcctAmtPair(buf.decBytes());
cnts[6]++;
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder Slash
struct AcctAmtPair {
address account; // tag: 1
uint256 amount; // tag: 2
} // end struct AcctAmtPair
function decAcctAmtPair(bytes memory raw) internal pure returns (AcctAmtPair memory m) {
Pb.Buffer memory buf = Pb.fromBytes(raw);
uint256 tag;
Pb.WireType wire;
while (buf.hasMore()) {
(tag, wire) = buf.decKey();
if (false) {}
// solidity has no switch/case
else if (tag == 1) {
m.account = Pb._address(buf.decBytes());
} else if (tag == 2) {
m.amount = Pb._uint256(buf.decBytes());
} else {
buf.skipValue(wire);
} // skip value of unknown tag
}
} // end decoder AcctAmtPair
}
================================================
FILE: contracts/libraries/Utils.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
library Utils {
// https://ethereum.stackexchange.com/a/83577
// https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol
function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {
// If the _res length is less than 68, then the transaction failed silently (without a revert message)
if (_returnData.length < 68) return "Transaction reverted silently";
assembly {
// Slice the sighash.
_returnData := add(_returnData, 0x04)
}
return abi.decode(_returnData, (string)); // All that remains is the revert string
}
}
================================================
FILE: contracts/libraries/proto/README.md
================================================
# about soltype
Due to pb gen sol only supports soltype field option defined in the same proto file. When we have multiple proto files that need soltype, it will break on go side. The short term solution is to manually sync proto files to go repo, and remove soltype related.
## proper solution
have a single file like soltype.proto define the field option, then other proto files just import it. pb gen sol need to parse imported proto and ExtName will have soltype.proto package prefix eg. opt.soltype instead of just soltype. unless all protos have same package which will cause issue for pb gen sol as it outputs .sol files based on proto package name.
or maybe we just register the fieldoption w/ proto team officially? still need to import another proto but package will be celer.opt
https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/descriptor.proto#L325
# Generating Solidity bindings
From the project repo root, run:
```sh
protoc --sol_out=importpb=true:contracts/libraries contracts/libraries/proto/{filename}.proto
```
================================================
FILE: contracts/libraries/proto/bridge.proto
================================================
syntax = "proto3";
option go_package = "github.com/celer-network/sgn-v2/proto/eth";
import "google/protobuf/descriptor.proto";
package bridge;
extend google.protobuf.FieldOptions {
string soltype = 1004;
}
message Relay {
bytes sender = 1 [(soltype) = "address"];
bytes receiver = 2 [(soltype) = "address"];
bytes token = 3 [(soltype) = "address"]; // asset address on dest chain
bytes amount = 4 [(soltype) = "uint256"];
uint64 src_chain_id = 5;
uint64 dst_chain_id = 6;
bytes src_transfer_id = 7 [(soltype) = "bytes32"];
}
================================================
FILE: contracts/libraries/proto/farming.proto
================================================
syntax = "proto3";
option go_package = "github.com/celer-network/sgn-v2/proto/eth";
import "google/protobuf/descriptor.proto";
package farming;
extend google.protobuf.FieldOptions {
string soltype = 1004;
}
message FarmingRewards {
// recipient defines the reward recipient
bytes recipient = 1 [(soltype) = "address"];
// token_addresses defines the list of reward token addresses
repeated bytes token_addresses = 2 [(soltype) = "address"];
// cumulative_reward_amounts defines the cumulative amount of rewards
repeated bytes cumulative_reward_amounts = 3 [(soltype) = "uint256"];
}
================================================
FILE: contracts/libraries/proto/pegged.proto
================================================
syntax = "proto3";
option go_package = "github.com/celer-network/sgn-v2/proto/eth";
import "google/protobuf/descriptor.proto";
package pegged;
extend google.protobuf.FieldOptions {
string soltype = 1004;
}
// mint pegged token
// triggered by deposit at the original token vault
message Mint {
bytes token = 1 [(soltype) = "address"];
bytes account = 2 [(soltype) = "address"];
bytes amount = 3 [(soltype) = "uint256"];
// depositor defines the account address that made deposit at the original token chain,
// or the account address that burned tokens at another remote chain
// Not applicable to governance-triggered mints.
bytes depositor = 4 [(soltype) = "address"];
// ref_chain_id defines the reference chain ID, taking values of:
// 1. The common case: the chain ID on which the corresponding deposit or burn happened;
// 2. Governance-triggered mint: the chain ID on which the minting will happen.
uint64 ref_chain_id = 5;
// ref_id defines a unique reference ID, taking values of:
// 1. The common case of deposit/burn-mint: the deposit or burn ID;
// 2. Governance-triggered mint: ID as needed.
bytes ref_id = 6 [(soltype) = "bytes32"];
}
// withdraw locked original tokens
// triggered by burn at the pegged token bridge
message Withdraw {
bytes token = 1 [(soltype) = "address"];
bytes receiver = 2 [(soltype) = "address"];
bytes amount = 3 [(soltype) = "uint256"];
// burn_account defines the account that burned the pegged token.
// Not applicable to fee claims and governance-triggered withdrawals.
bytes burn_account = 4 [(soltype) = "address"];
// ref_chain_id defines the reference chain ID, taking values of:
// 1. The common case of burn-withdraw: the chain ID on which the corresponding burn happened;
// 2. Pegbridge fee claim: zero / Not applicable;
// 3. Other governance-triggered withdrawals: the chain ID on which the withdrawal will happen.
uint64 ref_chain_id = 5;
// ref_id defines a unique reference ID, taking values of:
// 1. The common case of burn-withdraw: the burn ID;
// 2. Pegbridge fee claim: a per-account nonce;
// 3. Refund for wrong deposit: the deposit ID;
// 4. Governance-triggered withdrawal: ID as needed.
bytes ref_id = 6 [(soltype) = "bytes32"];
}
================================================
FILE: contracts/libraries/proto/pool.proto
================================================
syntax = "proto3";
option go_package = "github.com/celer-network/sgn-v2/proto/eth";
import "google/protobuf/descriptor.proto";
package pool;
extend google.protobuf.FieldOptions {
string soltype = 1004;
}
message WithdrawMsg {
uint64 chainid = 1;
uint64 seqnum = 2; // global unique across chains
bytes receiver = 3 [(soltype) = "address"];
bytes token = 4 [(soltype) = "address"];
bytes amount = 5 [(soltype) = "uint256"];
// reference id
// 1. if refund, set as xfer_id
// 2. if claim fee, set as 0x1
// 3. if LP withdraw liquidity, set as 0x0
bytes refid = 6 [(soltype) = "bytes32"];
}
================================================
FILE: contracts/libraries/proto/sgn.proto
================================================
syntax = "proto3";
option go_package = "github.com/celer-network/sgn-v2/proto/eth";
import "google/protobuf/descriptor.proto";
package sgn;
extend google.protobuf.FieldOptions {
string soltype = 1004;
}
message Withdrawal {
bytes account = 1 [(soltype) = "address"];
bytes token = 2 [(soltype) = "address"];
bytes cumulative_amount = 3 [(soltype) = "uint256"];
}
================================================
FILE: contracts/libraries/proto/staking.proto
================================================
syntax = "proto3";
option go_package = "github.com/celer-network/sgn-v2/proto/eth";
import "google/protobuf/descriptor.proto";
package staking;
extend google.protobuf.FieldOptions {
string soltype = 1004;
}
message StakingReward {
bytes recipient = 1 [(soltype) = "address"];
bytes cumulative_reward_amount = 2 [(soltype) = "uint256"];
}
message Slash {
bytes validator = 1 [(soltype) = "address"];
uint64 nonce = 2;
uint64 slash_factor = 3;
uint64 expire_time = 4; // block timestamp
uint64 jail_period = 5; // block number
repeated AcctAmtPair collectors = 6;
}
message AcctAmtPair {
bytes account = 1 [(soltype) = "address"];
bytes amount = 2 [(soltype) = "uint256"];
}
================================================
FILE: contracts/liquidity-bridge/Bridge.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../libraries/PbBridge.sol";
import "./Pool.sol";
/**
* @title The liquidity-pool based bridge.
*/
contract Bridge is Pool {
using SafeERC20 for IERC20;
// liquidity events
event Send(
bytes32 transferId,
address sender,
address receiver,
address token,
uint256 amount,
uint64 dstChainId,
uint64 nonce,
uint32 maxSlippage
);
event Relay(
bytes32 transferId,
address sender,
address receiver,
address token,
uint256 amount,
uint64 srcChainId,
bytes32 srcTransferId
);
// gov events
event MinSendUpdated(address token, uint256 amount);
event MaxSendUpdated(address token, uint256 amount);
mapping(bytes32 => bool) public transfers;
mapping(address => uint256) public minSend; // send _amount must > minSend
mapping(address => uint256) public maxSend;
// min allowed max slippage uint32 value is slippage * 1M, eg. 0.5% -> 5000
uint32 public minimalMaxSlippage;
/**
* @notice Send a cross-chain transfer via the liquidity pool-based bridge.
* NOTE: This function DOES NOT SUPPORT fee-on-transfer / rebasing tokens.
* @param _receiver The address of the receiver.
* @param _token The address of the token.
* @param _amount The amount of the transfer.
* @param _dstChainId The destination chain ID.
* @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.
* @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.
* Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least (100% - max slippage percentage) * amount or the
* transfer can be refunded.
*/
function send(
address _receiver,
address _token,
uint256 _amount,
uint64 _dstChainId,
uint64 _nonce,
uint32 _maxSlippage // slippage * 1M, eg. 0.5% -> 5000
) external nonReentrant whenNotPaused {
bytes32 transferId = _send(_receiver, _token, _amount, _dstChainId, _nonce, _maxSlippage);
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
emit Send(transferId, msg.sender, _receiver, _token, _amount, _dstChainId, _nonce, _maxSlippage);
}
/**
* @notice Send a cross-chain transfer via the liquidity pool-based bridge using the native token.
* @param _receiver The address of the receiver.
* @param _amount The amount of the transfer.
* @param _dstChainId The destination chain ID.
* @param _nonce A unique number. Can be timestamp in practice.
* @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.
* Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least (100% - max slippage percentage) * amount or the
* transfer can be refunded.
*/
function sendNative(
address _receiver,
uint256 _amount,
uint64 _dstChainId,
uint64 _nonce,
uint32 _maxSlippage
) external payable nonReentrant whenNotPaused {
require(msg.value == _amount, "Amount mismatch");
require(nativeWrap != address(0), "Native wrap not set");
bytes32 transferId = _send(_receiver, nativeWrap, _amount, _dstChainId, _nonce, _maxSlippage);
IWETH(nativeWrap).deposit{value: _amount}();
emit Send(transferId, msg.sender, _receiver, nativeWrap, _amount, _dstChainId, _nonce, _maxSlippage);
}
function _send(
address _receiver,
address _token,
uint256 _amount,
uint64 _dstChainId,
uint64 _nonce,
uint32 _maxSlippage
) private returns (bytes32) {
require(_amount > minSend[_token], "amount too small");
require(maxSend[_token] == 0 || _amount <= maxSend[_token], "amount too large");
require(_maxSlippage > minimalMaxSlippage, "max slippage too small");
bytes32 transferId = keccak256(
// uint64(block.chainid) for consistency as entire system uses uint64 for chain id
// len = 20 + 20 + 20 + 32 + 8 + 8 + 8 = 116
abi.encodePacked(msg.sender, _receiver, _token, _amount, _dstChainId, _nonce, uint64(block.chainid))
);
require(transfers[transferId] == false, "transfer exists");
transfers[transferId] = true;
return transferId;
}
/**
* @notice Relay a cross-chain transfer sent from a liquidity pool-based bridge on another chain.
* @param _relayRequest The serialized Relay protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by
* +2/3 of the bridge's current signing power to be delivered.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
*/
function relay(
bytes calldata _relayRequest,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external whenNotPaused {
bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), "Relay"));
verifySigs(abi.encodePacked(domain, _relayRequest), _sigs, _signers, _powers);
PbBridge.Relay memory request = PbBridge.decRelay(_relayRequest);
// len = 20 + 20 + 20 + 32 + 8 + 8 + 32 = 140
bytes32 transferId = keccak256(
abi.encodePacked(
request.sender,
request.receiver,
request.token,
request.amount,
request.srcChainId,
request.dstChainId,
request.srcTransferId
)
);
require(transfers[transferId] == false, "transfer exists");
transfers[transferId] = true;
_updateVolume(request.token, request.amount);
uint256 delayThreshold = delayThresholds[request.token];
if (delayThreshold > 0 && request.amount > delayThreshold) {
_addDelayedTransfer(transferId, request.receiver, request.token, request.amount);
} else {
_sendToken(request.receiver, request.token, request.amount);
}
emit Relay(
transferId,
request.sender,
request.receiver,
request.token,
request.amount,
request.srcChainId,
request.srcTransferId
);
}
function setMinSend(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {
require(_tokens.length == _amounts.length, "length mismatch");
for (uint256 i = 0; i < _tokens.length; i++) {
minSend[_tokens[i]] = _amounts[i];
emit MinSendUpdated(_tokens[i], _amounts[i]);
}
}
function setMaxSend(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {
require(_tokens.length == _amounts.length, "length mismatch");
for (uint256 i = 0; i < _tokens.length; i++) {
maxSend[_tokens[i]] = _amounts[i];
emit MaxSendUpdated(_tokens[i], _amounts[i]);
}
}
function setMinimalMaxSlippage(uint32 _minimalMaxSlippage) external onlyGovernor {
minimalMaxSlippage = _minimalMaxSlippage;
}
// This is needed to receive ETH when calling `IWETH.withdraw`
receive() external payable {}
}
================================================
FILE: contracts/liquidity-bridge/FarmingRewards.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/ISigsVerifier.sol";
import "../libraries/PbFarming.sol";
import "../safeguard/Pauser.sol";
/**
* @title A contract to hold and distribute farming rewards.
*/
contract FarmingRewards is Pauser {
using SafeERC20 for IERC20;
ISigsVerifier public immutable sigsVerifier;
// recipient => tokenAddress => amount
mapping(address => mapping(address => uint256)) public claimedRewardAmounts;
event FarmingRewardClaimed(address indexed recipient, address indexed token, uint256 reward);
event FarmingRewardContributed(address indexed contributor, address indexed token, uint256 contribution);
constructor(ISigsVerifier _sigsVerifier) {
sigsVerifier = _sigsVerifier;
}
/**
* @notice Claim rewards
* @dev Here we use cumulative reward to make claim process idempotent
* @param _rewardsRequest rewards request bytes coded in protobuf
* @param _sigs list of signatures sorted by signer addresses in ascending order
* @param _signers sorted list of current signers
* @param _powers powers of current signers
*/
function claimRewards(
bytes calldata _rewardsRequest,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external whenNotPaused {
bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), "FarmingRewards"));
sigsVerifier.verifySigs(abi.encodePacked(domain, _rewardsRequest), _sigs, _signers, _powers);
PbFarming.FarmingRewards memory rewards = PbFarming.decFarmingRewards(_rewardsRequest);
bool hasNewReward;
for (uint256 i = 0; i < rewards.tokenAddresses.length; i++) {
address token = rewards.tokenAddresses[i];
uint256 cumulativeRewardAmount = rewards.cumulativeRewardAmounts[i];
uint256 newReward = cumulativeRewardAmount - claimedRewardAmounts[rewards.recipient][token];
if (newReward > 0) {
hasNewReward = true;
claimedRewardAmounts[rewards.recipient][token] = cumulativeRewardAmount;
IERC20(token).safeTransfer(rewards.recipient, newReward);
emit FarmingRewardClaimed(rewards.recipient, token, newReward);
}
}
require(hasNewReward, "No new reward");
}
/**
* @notice Contribute reward tokens to the reward pool
* @param _token the address of the token to contribute
* @param _amount the amount of the token to contribute
*/
function contributeToRewardPool(address _token, uint256 _amount) external whenNotPaused {
address contributor = msg.sender;
IERC20(_token).safeTransferFrom(contributor, address(this), _amount);
emit FarmingRewardContributed(contributor, _token, _amount);
}
/**
* @notice Owner drains tokens when the contract is paused
* @dev emergency use only
* @param _token the address of the token to drain
* @param _amount drained token amount
*/
function drainToken(address _token, uint256 _amount) external whenPaused onlyOwner {
IERC20(_token).safeTransfer(msg.sender, _amount);
}
}
================================================
FILE: contracts/liquidity-bridge/Pool.sol
================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../interfaces/IWETH.sol";
import "../libraries/PbPool.sol";
import "../safeguard/Pauser.sol";
import "../safeguard/VolumeControl.sol";
import "../safeguard/DelayedTransfer.sol";
import "./Signers.sol";
/**
* @title Liquidity pool functions for {Bridge}.
*/
contract Pool is Signers, ReentrancyGuard, Pauser, VolumeControl, DelayedTransfer {
using SafeERC20 for IERC20;
uint64 public addseq; // ensure unique LiquidityAdded event, start from 1
mapping(address => uint256) public minAdd; // add _amount must > minAdd
// map of successful withdraws, if true means already withdrew money or added to delayedTransfers
mapping(bytes32 => bool) public withdraws;
// erc20 wrap of gas token of this chain, eg. WETH, when relay ie. pay out,
// if request.token equals this, will withdraw and send native token to receiver
// note we don't check whether it's zero address. when this isn't set, and request.token
// is all 0 address, guarantee fail
address public nativeWrap;
// when transfer native token after wrap, use this gas used config.
uint256 public nativeTokenTransferGas = 50000;
// liquidity events
event LiquidityAdded(
uint64 seqnum,
address provider,
address token,
uint256 amount // how many tokens were added
);
event WithdrawDone(
bytes32 withdrawId,
uint64 seqnum,
address receiver,
address token,
uint256 amount,
bytes32 refid
);
event MinAddUpdated(address token, uint256 amount);
/**
* @notice Add liquidity to the pool-based bridge.
* NOTE: This function DOES NOT SUPPORT fee-on-transfer / rebasing tokens.
* @param _token The address of the token.
* @param _amount The amount to add.
*/
function addLiquidity(address _token, uint256 _amount) external nonReentrant whenNotPaused {
require(_amount > minAdd[_token], "amount too small");
addseq += 1;
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
emit LiquidityAdded(addseq, msg.sender, _token, _amount);
}
/**
* @notice Add native token liquidity to the pool-based bridge.
* @param _amount The amount to add.
*/
function addNativeLiquidity(uint256 _amount) external payable nonReentrant whenNotPaused {
require(msg.value == _amount, "Amount mismatch");
require(nativeWrap != address(0), "Native wrap not set");
require(_amount > minAdd[nativeWrap], "amount too small");
addseq += 1;
IWETH(nativeWrap).deposit{value: _amount}();
emit LiquidityAdded(addseq, msg.sender, nativeWrap, _amount);
}
/**
* @notice Withdraw funds from the bridge pool.
* @param _wdmsg The serialized Withdraw protobuf.
* @param _sigs The list of signatures sorted by signing addresses in ascending order. A withdrawal must be
* signed-off by +2/3 of the bridge's current signing power to be delivered.
* @param _signers The sorted list of signers.
* @param _powers The signing powers of the signers.
*/
function withdraw(
bytes calldata _wdmsg,
bytes[] calldata _sigs,
address[] calldata _signers,
uint256[] calldata _powers
) external whenNotPaused {
bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), "WithdrawMsg"));
verifySigs(abi.encodePacked(domain, _wdmsg), _sigs, _signers, _powers);
// decode and check wdmsg
PbPool.WithdrawMsg memory wdmsg = PbPool.decWithdrawMsg(_wdmsg);
// len = 8 + 8 + 20 + 20 + 32 = 88
bytes32 wdId = keccak256(
abi.encodePacked(wdmsg.chainid, wdmsg.seqnum, wdmsg.receiver, wdmsg.token, wdmsg.amount)
);
require(withdraws[wdId] == false, "withdraw already succeeded");
withdraws[wdId] = true;
_updateVolume(wdmsg.token, wdmsg.amount);
uint256 delayThreshold = delayThresholds[wdmsg.token];
if (delayThreshold > 0 && wdmsg.amount > delayThreshold) {
_addDelayedTransfer(wdId, wdmsg.receiver, wdmsg.token, wdmsg.amount);
} else {
_sendToken(wdmsg.receiver, wdmsg.token, wdmsg.amount);
}
emit WithdrawDone(wdId, wdmsg.seqnum, wdmsg.receiver, wdmsg.token, wdmsg.amount, wdmsg.refid);
}
function executeDelayedTransfer(bytes32 id) external whenNotPaused {
delayedTransfer memory transfer = _executeDelayedTransfer(id);
_sendToken(transfer.receiver, transfer.token, transfer.amount);
}
function setMinAdd(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {
require(_tokens.length == _amounts.length, "length mismatch");
for (uint256 i = 0; i < _tokens.length; i++) {
minAdd[_tokens[i]] = _amounts[i];
emit MinAddUpdated(_tokens[i], _amounts[i]);
}
}
function _sendToken(
address _receiver,
address _token,
gitextract_l6jfhm9x/ ├── .eslintignore ├── .eslintrc.js ├── .github/ │ └── workflows/ │ ├── abigen.yml │ ├── pbsol.yml │ └── run_tests.yml ├── .gitignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── benchmark/ │ └── relay.ts ├── contracts/ │ ├── circle-usdc/ │ │ ├── CircleBridgeProxy.sol │ │ ├── CircleBridgeProxyV2.sol │ │ └── FeeOperator.sol │ ├── governed-owner/ │ │ ├── GovernedOwnerProxy.sol │ │ ├── SimpleGovernance.sol │ │ ├── customized/ │ │ │ └── MessageBusOwner.sol │ │ ├── interfaces/ │ │ │ ├── IBridgeOwner.sol │ │ │ ├── ICommonOwner.sol │ │ │ ├── IMessageOwner.sol │ │ │ ├── ISgnOwner.sol │ │ │ └── IUpgradeableOwner.sol │ │ └── proxies/ │ │ ├── BridgeOwnerProxy.sol │ │ ├── CommonOwnerProxy.sol │ │ ├── MessageOwnerProxy.sol │ │ ├── OwnerDataTypes.sol │ │ ├── OwnerProxyBase.sol │ │ ├── SgnOwnerProxy.sol │ │ └── UpgradeableOwnerProxy.sol │ ├── integration-examples/ │ │ ├── ContractAsLP.sol │ │ └── ContractAsSender.sol │ ├── interfaces/ │ │ ├── IBridge.sol │ │ ├── ICircleBridge.sol │ │ ├── IDelayedTransfer.sol │ │ ├── IOriginalTokenVault.sol │ │ ├── IOriginalTokenVaultV2.sol │ │ ├── IPeggedToken.sol │ │ ├── IPeggedTokenBridge.sol │ │ ├── IPeggedTokenBridgeV2.sol │ │ ├── IPeggedTokenBurnFrom.sol │ │ ├── IPool.sol │ │ ├── ISigsVerifier.sol │ │ ├── IUniswapV2.sol │ │ ├── IWETH.sol │ │ └── IWithdrawInbox.sol │ ├── libraries/ │ │ ├── BridgeTransferLib.sol │ │ ├── Pb.sol │ │ ├── PbBridge.sol │ │ ├── PbFarming.sol │ │ ├── PbPegged.sol │ │ ├── PbPool.sol │ │ ├── PbSgn.sol │ │ ├── PbStaking.sol │ │ ├── Utils.sol │ │ └── proto/ │ │ ├── README.md │ │ ├── bridge.proto │ │ ├── farming.proto │ │ ├── pegged.proto │ │ ├── pool.proto │ │ ├── sgn.proto │ │ └── staking.proto │ ├── liquidity-bridge/ │ │ ├── Bridge.sol │ │ ├── FarmingRewards.sol │ │ ├── Pool.sol │ │ ├── Signers.sol │ │ └── WithdrawInbox.sol │ ├── message/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── apps/ │ │ │ ├── RFQ.sol │ │ │ ├── adapter/ │ │ │ │ └── MessageReceiverAdapter.sol │ │ │ ├── examples/ │ │ │ │ ├── BatchTransfer.sol │ │ │ │ ├── MsgExampleBasic.sol │ │ │ │ ├── MsgExampleBasicTransfer.sol │ │ │ │ ├── MsgExampleInOrder.sol │ │ │ │ ├── MsgTest.sol │ │ │ │ ├── TransferSwap.sol │ │ │ │ └── TransferSwapSendBack.sol │ │ │ └── nft-bridge/ │ │ │ ├── MCNNFT.sol │ │ │ ├── NFTBridge.sol │ │ │ ├── OrigNFT.sol │ │ │ └── PegNFT.sol │ │ ├── framework/ │ │ │ ├── MessageApp.sol │ │ │ ├── MessageBusAddress.sol │ │ │ ├── MessageReceiverApp.sol │ │ │ └── MessageSenderApp.sol │ │ ├── interfaces/ │ │ │ ├── IMessageBus.sol │ │ │ └── IMessageReceiverApp.sol │ │ ├── libraries/ │ │ │ ├── MessageSenderLib.sol │ │ │ └── MsgDataTypes.sol │ │ ├── messagebus/ │ │ │ ├── MessageBus.sol │ │ │ ├── MessageBusReceiver.sol │ │ │ └── MessageBusSender.sol │ │ └── safeguard/ │ │ ├── DelayedMessage.sol │ │ └── MessageAppPauser.sol │ ├── miscs/ │ │ ├── Faucet.sol │ │ ├── MintableERC20.sol │ │ ├── oasys/ │ │ │ ├── L1StandardERC20.sol │ │ │ ├── L1StandardERC20Factory.sol │ │ │ └── README.md │ │ └── proxy-admin/ │ │ ├── Ownable.sol │ │ └── ProxyAdmin.sol │ ├── pegged-bridge/ │ │ ├── OriginalTokenVault.sol │ │ ├── OriginalTokenVaultV2.sol │ │ ├── PeggedBrc20Bridge.sol │ │ ├── PeggedTokenBridge.sol │ │ ├── PeggedTokenBridgeV2.sol │ │ ├── README.md │ │ ├── customized/ │ │ │ └── PeggedNativeTokenBridge.sol │ │ └── tokens/ │ │ ├── ERC20Permit/ │ │ │ ├── MintSwapCanonicalTokenPermit.sol │ │ │ ├── MultiBridgeTokenPermit.sol │ │ │ └── SingleBridgeTokenPermit.sol │ │ ├── IntermediaryBridgeToken.sol │ │ ├── IntermediaryOriginalToken.sol │ │ ├── MintSwapCanonicalToken.sol │ │ ├── MintSwapCanonicalTokenUpgradable.sol │ │ ├── MultiBridgeToken.sol │ │ ├── SingleBridgeToken.sol │ │ ├── SwapBridgeToken.sol │ │ ├── WrappedBridgeToken.sol │ │ ├── customized/ │ │ │ ├── CircleBridgeToken.sol │ │ │ ├── FraxBridgeToken.sol │ │ │ ├── MaiBridgeToken.sol │ │ │ ├── OntologyBridgeToken.sol │ │ │ └── xc20/ │ │ │ ├── XC20BridgeHub.sol │ │ │ ├── XC20BridgeToken.sol │ │ │ └── interfaces/ │ │ │ └── IXC20BridgeHub.sol │ │ ├── freezable/ │ │ │ ├── Freezable.sol │ │ │ ├── MintSwapCanonicalTokenFreezable.sol │ │ │ └── MintSwapCanonicalTokenUpgradableFreezable.sol │ │ └── owners/ │ │ └── RestrictedMultiBridgeTokenOwner.sol │ ├── proxy/ │ │ └── TransferAgent.sol │ ├── safeguard/ │ │ ├── DelayedTransfer.sol │ │ ├── Governor.sol │ │ ├── Ownable.sol │ │ ├── Pauser.sol │ │ ├── VolumeControl.sol │ │ ├── Whitelist.sol │ │ └── sentinel/ │ │ ├── Guard.sol │ │ ├── GuardedGovernor.sol │ │ ├── GuardedPauser.sol │ │ └── Sentinel.sol │ ├── staking/ │ │ ├── DataTypes.sol │ │ ├── Govern.sol │ │ ├── SGN.sol │ │ ├── Staking.sol │ │ ├── StakingReward.sol │ │ └── Viewer.sol │ └── test-helpers/ │ ├── DummySwap.sol │ ├── TestERC20.sol │ └── WETH.sol ├── deploy/ │ ├── circle-usdc/ │ │ ├── 000_circle_bridge_proxy.ts │ │ └── 000_circle_bridge_proxy_v2.ts │ ├── core/ │ │ └── 000_sgn_staking.ts │ ├── governed-owner/ │ │ ├── 000_governed_owner.ts │ │ └── customized/ │ │ └── 000_message_bus_owner.ts │ ├── liquidity-bridge/ │ │ ├── 000_bridge.ts │ │ └── 001_farming_rewards.ts │ ├── message/ │ │ ├── 000_message_bus_init.ts │ │ ├── 001_message_bus.ts │ │ └── apps/ │ │ ├── 000_transfer_swap.ts │ │ ├── 001_nft_bridge.ts │ │ ├── 002_peg_nft.ts │ │ ├── 003_orig_nft.ts │ │ ├── 004_mcn_nft.ts │ │ ├── 005_msg_test.ts │ │ ├── 006_rfq.ts │ │ └── 007_adapter.ts │ ├── miscs/ │ │ ├── 000_test_token.ts │ │ ├── 001_faucet.ts │ │ ├── 002_dummy_swap.ts │ │ ├── 003_withdraw_inbox.ts │ │ ├── 004_contract_as_lp.ts │ │ ├── 005_contract_as_sender.ts │ │ ├── 006_transfer_agent.ts │ │ └── 007_default_proxy_admin.ts │ ├── pegged-bridge/ │ │ ├── 000_original_token_vault.ts │ │ ├── 001_pegged_token_bridge.ts │ │ ├── 002_original_token_vault_v2.ts │ │ ├── 003_pegged_token_bridge_v2.ts │ │ ├── 004_pegged_brc20_bridge.ts │ │ ├── customized/ │ │ │ └── 000_pegged_native_token_bridge.ts │ │ └── tokens/ │ │ ├── 000_single_bridge_token.ts │ │ ├── 001_single_bridge_token_permit.ts │ │ ├── 002_multi_bridge_token.ts │ │ ├── 003_multi_bridge_token_permit.ts │ │ ├── 004_mint_swap_canonical_token.ts │ │ ├── 005_mint_swap_canonical_token_permit.ts │ │ ├── 006_mint_swap_canonical_token_upgradable.ts │ │ ├── 007_wrapped_bridge_token.ts │ │ ├── 008_intermediary_bridge_token.ts │ │ ├── 009_intermediary_original_token.ts │ │ ├── 010_circle_bridge_token.ts │ │ ├── customized/ │ │ │ ├── 000_frax_bridge_token.ts │ │ │ ├── 001_mai_bridge_token.ts │ │ │ └── 002_ontology_bridge_token.ts │ │ ├── freezable/ │ │ │ ├── 000_mint_swap_canonical_token_upgradable_freezable.ts │ │ │ └── 001_mint_swap_canonical_token_freezable.ts │ │ └── owners/ │ │ └── 000_restricted_multi_bridge_token_owner.ts │ └── sentinel/ │ ├── 000_sentinel.ts │ └── 001_sentinel_zksync.ts ├── hardhat.config.ts ├── import-sorter.json ├── package.json ├── reports/ │ ├── contract_sizes.txt │ └── gas_usage/ │ ├── relay.txt │ └── summary.txt ├── scripts/ │ ├── common.ts │ ├── init_governed_owner.ts │ ├── oasys_token_factory.ts │ ├── pb_gen_sol.sh │ ├── reset_signers.ts │ ├── sentinel/ │ │ ├── sentinel_guard.ts │ │ ├── sentinel_relax.ts │ │ └── sentinel_set_limits.ts │ ├── set_basics.ts │ ├── set_limits.ts │ ├── set_msg_fee.ts │ ├── solc_abigen.sh │ ├── solt.sh │ ├── transfer_ownership.ts │ └── update_bridge_supply_cap.ts ├── test/ │ ├── Basics.spec.ts │ ├── Bridge.spec.ts │ ├── FarmingRewards.spec.ts │ ├── Governance.spec.ts │ ├── GovernedOwner.spec.ts │ ├── Message.spec.ts │ ├── MultiValidator.spec.ts │ ├── Sentinel.spec.ts │ ├── Slash.spec.ts │ ├── StakingReward.spec.ts │ ├── TransferSwap.spec.ts │ ├── ValidatorSigner.spec.ts │ └── lib/ │ ├── common.ts │ ├── constants.ts │ └── proto.ts └── tsconfig.json
SYMBOL INDEX (130 symbols across 29 files)
FILE: benchmark/relay.ts
constant GAS_USAGE_DIR (line 15) | const GAS_USAGE_DIR = 'reports/gas_usage/';
constant GAS_USAGE_LOG (line 16) | const GAS_USAGE_LOG = path.join(GAS_USAGE_DIR, 'relay.txt');
function fixture (line 25) | async function fixture() {
function getPowers (line 60) | async function getPowers(
function doBenchmarkRelaySig (line 80) | async function doBenchmarkRelaySig(signerNum: number) {
function doBenchmarkRelaySigner (line 95) | async function doBenchmarkRelaySigner(maxSignerNum: number, quorumSigNum...
function doBenchmarkRelay (line 110) | async function doBenchmarkRelay(signerNum: number, quorumSigNum: number) {
FILE: hardhat.config.ts
constant DEFAULT_ENDPOINT (line 21) | const DEFAULT_ENDPOINT = 'http://localhost:8545';
constant DEFAULT_PRIVATE_KEY (line 22) | const DEFAULT_PRIVATE_KEY =
function getNetworkConfig (line 261) | function getNetworkConfig(url: string, kmsKeyId: string, privateKey: str...
FILE: scripts/common.ts
function getDeployerSigner (line 6) | async function getDeployerSigner(): Promise<SignerWithAddress> {
function getFeeOverrides (line 11) | async function getFeeOverrides(): Promise<Overrides> {
function getParseUnitsCallback (line 24) | function getParseUnitsCallback(
FILE: scripts/init_governed_owner.ts
function initGov (line 10) | async function initGov(): Promise<void> {
FILE: scripts/oasys_token_factory.ts
function createToken (line 9) | async function createToken(): Promise<void> {
FILE: scripts/reset_signers.ts
function resetSigners (line 10) | async function resetSigners(): Promise<void> {
FILE: scripts/sentinel/sentinel_guard.ts
function guard (line 10) | async function guard(): Promise<void> {
FILE: scripts/sentinel/sentinel_relax.ts
function relax (line 10) | async function relax(): Promise<void> {
FILE: scripts/sentinel/sentinel_set_limits.ts
function setLimitIfSpecified (line 12) | async function setLimitIfSpecified(
function setBridgeLimits (line 40) | async function setBridgeLimits(sentinelAddr: string): Promise<void> {
function setOriginalTokenVaultLimits (line 103) | async function setOriginalTokenVaultLimits(sentinelAddr: string): Promis...
function setPeggedTokenBridgeLimits (line 157) | async function setPeggedTokenBridgeLimits(sentinelAddr: string): Promise...
function setLimits (line 211) | async function setLimits(): Promise<void> {
FILE: scripts/set_basics.ts
function setBridgeBasics (line 17) | async function setBridgeBasics(): Promise<void> {
function setPeggedTokenBridgeBasics (line 64) | async function setPeggedTokenBridgeBasics(): Promise<void> {
function setPeggedNativeTokenBridgeBasics (line 101) | async function setPeggedNativeTokenBridgeBasics(): Promise<void> {
function setOriginalTokenVaultBasics (line 117) | async function setOriginalTokenVaultBasics(): Promise<void> {
function setMessageBusBasics (line 159) | async function setMessageBusBasics(): Promise<void> {
function setRFQBasics (line 195) | async function setRFQBasics(): Promise<void> {
function setBasics (line 244) | async function setBasics(): Promise<void> {
FILE: scripts/set_limits.ts
function setLimitIfSpecified (line 13) | async function setLimitIfSpecified(
function setBridgeLimits (line 35) | async function setBridgeLimits(): Promise<void> {
function setOriginalTokenVaultLimits (line 93) | async function setOriginalTokenVaultLimits(): Promise<void> {
function setPeggedTokenBridgeLimits (line 143) | async function setPeggedTokenBridgeLimits(): Promise<void> {
function setLimits (line 193) | async function setLimits(): Promise<void> {
FILE: scripts/set_msg_fee.ts
function setMsgFees (line 11) | async function setMsgFees(): Promise<void> {
FILE: scripts/transfer_ownership.ts
function transferOwnership (line 10) | async function transferOwnership(): Promise<void> {
FILE: scripts/update_bridge_supply_cap.ts
function updateBridgeSupplyCap (line 10) | async function updateBridgeSupplyCap(): Promise<void> {
FILE: test/Basics.spec.ts
function fixture (line 13) | async function fixture() {
FILE: test/Bridge.spec.ts
function getUpdateSignersSigs (line 11) | async function getUpdateSignersSigs(
function fixture (line 30) | async function fixture() {
FILE: test/FarmingRewards.spec.ts
function fixture (line 13) | async function fixture() {
FILE: test/Governance.spec.ts
function fixture (line 13) | async function fixture() {
FILE: test/GovernedOwner.spec.ts
function fixture (line 16) | async function fixture() {
FILE: test/Message.spec.ts
type RouteInfoStruct (line 21) | type RouteInfoStruct = {
type RouteInfo2Struct (line 28) | type RouteInfo2Struct = {
function computeMessageIdAndSigs (line 37) | async function computeMessageIdAndSigs(
function computeMessage2IdAndSigs (line 55) | async function computeMessage2IdAndSigs(
function getMessageWithTransferRequest (line 73) | async function getMessageWithTransferRequest(
function fixture (line 143) | async function fixture() {
FILE: test/MultiValidator.spec.ts
function fixture (line 12) | async function fixture() {
FILE: test/Sentinel.spec.ts
function fixture (line 11) | async function fixture() {
FILE: test/Slash.spec.ts
function fixture (line 14) | async function fixture() {
FILE: test/StakingReward.spec.ts
function fixture (line 13) | async function fixture() {
FILE: test/TransferSwap.spec.ts
constant UINT64_MAX (line 12) | const UINT64_MAX = '9223372036854775807';
function swapFixture (line 16) | async function swapFixture() {
function computeId (line 22) | function computeId(sender: string, srcChainId: number, dstChainId: numbe...
function computeDirectSwapId (line 26) | function computeDirectSwapId(sender: string, srcChainId: number, receive...
function encodeMessage (line 35) | function encodeMessage(
function slip (line 48) | function slip(amount: bigint, perc: number) {
type Swap (line 69) | interface Swap {
function prepare (line 78) | async function prepare() {
FILE: test/ValidatorSigner.spec.ts
function fixture (line 13) | async function fixture() {
FILE: test/lib/common.ts
type DeploymentInfo (line 45) | interface DeploymentInfo {
function deployContracts (line 55) | async function deployContracts(admin: ContractRunner): Promise<Deploymen...
type BridgeInfo (line 96) | interface BridgeInfo {
function deployBridgeContracts (line 103) | async function deployBridgeContracts(admin: ContractRunner): Promise<Bri...
type MessageInfo (line 123) | interface MessageInfo {
function deployMessageContracts (line 130) | async function deployMessageContracts(admin: ContractRunner): Promise<Me...
type GovernedOwnerInfo (line 149) | interface GovernedOwnerInfo {
function deployGovernedOwner (line 154) | async function deployGovernedOwner(
function deploySentinel (line 177) | async function deploySentinel(admin: ContractRunner): Promise<Sentinel> {
type SwapInfo (line 183) | interface SwapInfo {
function deploySwapContracts (line 192) | async function deploySwapContracts(admin: ContractRunner): Promise<SwapI...
function getAccounts (line 222) | async function getAccounts(admin: AbstractSigner, assets: TestERC20[], n...
function advanceBlockNumber (line 238) | async function advanceBlockNumber(blkNum: number): Promise<void> {
function advanceBlockNumberTo (line 246) | async function advanceBlockNumberTo(target: number): Promise<void> {
function getBlockTime (line 255) | async function getBlockTime() {
function advanceBlockTime (line 264) | async function advanceBlockTime(blkTime: number) {
function getAddrs (line 270) | function getAddrs(signers: Wallet[]) {
FILE: test/lib/constants.ts
constant PROPOSAL_DEPOSIT (line 3) | const PROPOSAL_DEPOSIT = 100;
constant VOTING_PERIOD (line 4) | const VOTING_PERIOD = 20;
constant UNBONDING_PERIOD (line 5) | const UNBONDING_PERIOD = 50;
constant MAX_VALIDATOR_NUM (line 6) | const MAX_VALIDATOR_NUM = 7;
constant MIN_VALIDATOR_TOKENS (line 7) | const MIN_VALIDATOR_TOKENS = parseUnits('4');
constant ADVANCE_NOTICE_PERIOD (line 8) | const ADVANCE_NOTICE_PERIOD = 10;
constant VALIDATOR_BOND_INTERVAL (line 9) | const VALIDATOR_BOND_INTERVAL = 0;
constant MAX_SLASH_FACTOR (line 10) | const MAX_SLASH_FACTOR = 1e5;
constant MIN_SELF_DELEGATION (line 12) | const MIN_SELF_DELEGATION = parseUnits('2');
constant VALIDATOR_STAKE (line 13) | const VALIDATOR_STAKE = parseUnits('1');
constant DELEGATOR_STAKE (line 14) | const DELEGATOR_STAKE = parseUnits('6');
constant COMMISSION_RATE (line 16) | const COMMISSION_RATE = 100;
constant SLASH_FACTOR (line 17) | const SLASH_FACTOR = 50000;
constant STATUS_UNBONDED (line 19) | const STATUS_UNBONDED = 1;
constant STATUS_UNBONDING (line 20) | const STATUS_UNBONDING = 2;
constant STATUS_BONDED (line 21) | const STATUS_BONDED = 3;
constant ENUM_PROPOSAL_DEPOSIT (line 23) | const ENUM_PROPOSAL_DEPOSIT = 0;
constant ENUM_VOTING_PERIOD (line 24) | const ENUM_VOTING_PERIOD = 1;
constant ENUM_UNBONDING_PERIOD (line 25) | const ENUM_UNBONDING_PERIOD = 2;
constant ENUM_MAX_VALIDATOR_NUM (line 26) | const ENUM_MAX_VALIDATOR_NUM = 3;
constant ENUM_MIN_VALIDATOR_TOKENS (line 27) | const ENUM_MIN_VALIDATOR_TOKENS = 4;
constant ENUM_MIN_SELF_DELEGATION (line 28) | const ENUM_MIN_SELF_DELEGATION = 5;
constant ENUM_ADVANCE_NOTICE_PERIOD (line 29) | const ENUM_ADVANCE_NOTICE_PERIOD = 6;
constant ENUM_VOTE_OPTION_UNVOTED (line 31) | const ENUM_VOTE_OPTION_UNVOTED = 0;
constant ENUM_VOTE_OPTION_YES (line 32) | const ENUM_VOTE_OPTION_YES = 1;
constant ENUM_VOTE_OPTION_ABSTAIN (line 33) | const ENUM_VOTE_OPTION_ABSTAIN = 2;
constant ENUM_VOTE_OPTION_NO (line 34) | const ENUM_VOTE_OPTION_NO = 3;
constant SUB_FEE (line 36) | const SUB_FEE = parseUnits('100000000', 'wei');
constant TYPE_MSG_XFER (line 38) | const TYPE_MSG_XFER = 0;
constant TYPE_MSG_ONLY (line 39) | const TYPE_MSG_ONLY = 1;
constant MSG_TX_NULL (line 41) | const MSG_TX_NULL = 0;
constant MSG_TX_SUCCESS (line 42) | const MSG_TX_SUCCESS = 1;
constant MSG_TX_FAIL (line 43) | const MSG_TX_FAIL = 2;
constant MSG_TX_FALLBACK (line 44) | const MSG_TX_FALLBACK = 3;
constant XFER_TYPE_LQ_RELAY (line 46) | const XFER_TYPE_LQ_RELAY = 1;
constant XFER_TYPE_LQ_WITHDRAW (line 47) | const XFER_TYPE_LQ_WITHDRAW = 2;
constant XFER_TYPE_PEG_MINT (line 48) | const XFER_TYPE_PEG_MINT = 3;
constant XFER_TYPE_PEG_WITHDRAW (line 49) | const XFER_TYPE_PEG_WITHDRAW = 4;
constant XFER_TYPE_PEGV2_MINT (line 50) | const XFER_TYPE_PEGV2_MINT = 5;
constant XFER_TYPE_PEGV2_WITHDRAW (line 51) | const XFER_TYPE_PEGV2_WITHDRAW = 6;
FILE: test/lib/proto.ts
type Proto (line 17) | interface Proto {
function getProtos (line 27) | async function getProtos(): Promise<Proto> {
function calculateSignatures (line 54) | async function calculateSignatures(signers: AbstractSigner[], hash: Uint...
function getStakingRewardRequest (line 63) | async function getStakingRewardRequest(
function getFarmingRewardsRequest (line 86) | async function getFarmingRewardsRequest(
function getAcctAmtPairs (line 114) | async function getAcctAmtPairs(accounts: string[], amounts: bigint[]): P...
function getSlashRequest (line 129) | async function getSlashRequest(
function getRelayRequest (line 163) | async function getRelayRequest(
function getWithdrawRequest (line 202) | async function getWithdrawRequest(
function getMintRequest (line 238) | async function getMintRequest(
Condensed preview — 236 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (871K chars).
[
{
"path": ".eslintignore",
"chars": 88,
"preview": "# folders\nartifacts/\nbin/\nbuild/\ncache/\ndeployments/\ngenfiles/\nnode_modules/\ntypechain/\n"
},
{
"path": ".eslintrc.js",
"chars": 1221,
"preview": "module.exports = {\n env: {\n es2021: true,\n mocha: true,\n node: true\n },\n root: true,\n extends: [\n 'prett"
},
{
"path": ".github/workflows/abigen.yml",
"chars": 766,
"preview": "# run solc/abigen for go binding\n\nname: gen-go-binding\non:\n pull_request:\n paths:\n - '.github/workflows/abigen."
},
{
"path": ".github/workflows/pbsol.yml",
"chars": 609,
"preview": "# run pb gen sol if proto files change\n\nname: pb-gen-sol\non:\n pull_request:\n paths:\n - '.github/workflows/pbsol"
},
{
"path": ".github/workflows/run_tests.yml",
"chars": 524,
"preview": "name: run-tests\non:\n pull_request:\n paths:\n - '.github/workflows/run_tests.yml'\n - 'hardhat.config.ts'\n "
},
{
"path": ".gitignore",
"chars": 226,
"preview": "artifacts/\nartifacts-zk/\nbin/\nbuild/\ncache/\ncache-zk/\ndeployments/hardhat/\ngenfiles/\nfailing_metadata/\nnode_modules/\ntyp"
},
{
"path": ".prettierrc.json",
"chars": 451,
"preview": "{\n \"trailingComma\": \"none\",\n \"tabWidth\": 2,\n \"semi\": true,\n \"singleQuote\": true,\n \"overrides\": [\n {\n \"files"
},
{
"path": "LICENSE",
"chars": 35149,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "README.md",
"chars": 6833,
"preview": "# SGN Contracts\n\nContracts for the Celer State Guardian Network (SGN) V2.\n\n### Run unit tests\n\n```sh\nyarn test\n```\n\n### "
},
{
"path": "benchmark/relay.ts",
"chars": 4865,
"preview": "import '@nomicfoundation/hardhat-ethers';\n\nimport { parseUnits, solidityPackedKeccak256, toNumber, Wallet } from 'ethers"
},
{
"path": "contracts/circle-usdc/CircleBridgeProxy.sol",
"chars": 4276,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.17;\n\nimport \"@openzeppelin/contracts/security/ReentrancyG"
},
{
"path": "contracts/circle-usdc/CircleBridgeProxyV2.sol",
"chars": 4382,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.17;\n\nimport \"@openzeppelin/contracts/security/ReentrancyG"
},
{
"path": "contracts/circle-usdc/FeeOperator.sol",
"chars": 1449,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.s"
},
{
"path": "contracts/governed-owner/GovernedOwnerProxy.sol",
"chars": 493,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./proxies/CommonOwnerProxy.sol\";\nimport \"./pr"
},
{
"path": "contracts/governed-owner/SimpleGovernance.sol",
"chars": 13567,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/governed-owner/customized/MessageBusOwner.sol",
"chars": 9885,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../../libraries/Utils.sol\";\nimport \"../interf"
},
{
"path": "contracts/governed-owner/interfaces/IBridgeOwner.sol",
"chars": 926,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IBridgeOwner {\n // for bridges\n\n fun"
},
{
"path": "contracts/governed-owner/interfaces/ICommonOwner.sol",
"chars": 262,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface ICommonOwner {\n function transferOwners"
},
{
"path": "contracts/governed-owner/interfaces/IMessageOwner.sol",
"chars": 533,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IMessageOwner {\n function setFeePerByte"
},
{
"path": "contracts/governed-owner/interfaces/ISgnOwner.sol",
"chars": 454,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface ISgnOwner {\n function setWhitelistEnabl"
},
{
"path": "contracts/governed-owner/interfaces/IUpgradeableOwner.sol",
"chars": 527,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IUpgradeableOwner {\n function changePro"
},
{
"path": "contracts/governed-owner/proxies/BridgeOwnerProxy.sol",
"chars": 5362,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./OwnerProxyBase.sol\";\nimport \"../interfaces/"
},
{
"path": "contracts/governed-owner/proxies/CommonOwnerProxy.sol",
"chars": 1606,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./OwnerProxyBase.sol\";\nimport \"../interfaces/"
},
{
"path": "contracts/governed-owner/proxies/MessageOwnerProxy.sol",
"chars": 2938,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./OwnerProxyBase.sol\";\nimport \"../interfaces/"
},
{
"path": "contracts/governed-owner/proxies/OwnerDataTypes.sol",
"chars": 344,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nlibrary OwnerDataTypes {\n enum Action {\n S"
},
{
"path": "contracts/governed-owner/proxies/OwnerProxyBase.sol",
"chars": 511,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"../SimpleGovernance.sol\";\n\nabstract contract"
},
{
"path": "contracts/governed-owner/proxies/SgnOwnerProxy.sol",
"chars": 3751,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./OwnerProxyBase.sol\";\nimport \"../interfaces/"
},
{
"path": "contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol",
"chars": 3238,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./OwnerProxyBase.sol\";\nimport \"../interfaces/"
},
{
"path": "contracts/integration-examples/ContractAsLP.sol",
"chars": 2860,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/integration-examples/ContractAsSender.sol",
"chars": 7869,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/interfaces/IBridge.sol",
"chars": 1458,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IBridge {\n function send(\n addre"
},
{
"path": "contracts/interfaces/ICircleBridge.sol",
"chars": 2611,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface ICircleBridge {\n /**\n * @notice Dep"
},
{
"path": "contracts/interfaces/IDelayedTransfer.sol",
"chars": 333,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\ninterface IDelayedTransfer {\n struct delayedTrans"
},
{
"path": "contracts/interfaces/IOriginalTokenVault.sol",
"chars": 1895,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IOriginalTokenVault {\n /**\n * @noti"
},
{
"path": "contracts/interfaces/IOriginalTokenVaultV2.sol",
"chars": 1951,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IOriginalTokenVaultV2 {\n /**\n * @no"
},
{
"path": "contracts/interfaces/IPeggedToken.sol",
"chars": 214,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IPeggedToken {\n function mint(address _"
},
{
"path": "contracts/interfaces/IPeggedTokenBridge.sol",
"chars": 1280,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IPeggedTokenBridge {\n /**\n * @notic"
},
{
"path": "contracts/interfaces/IPeggedTokenBridgeV2.sol",
"chars": 1809,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IPeggedTokenBridgeV2 {\n /**\n * @not"
},
{
"path": "contracts/interfaces/IPeggedTokenBurnFrom.sol",
"chars": 337,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\n// used for pegged token with openzeppelin ERC20Burn"
},
{
"path": "contracts/interfaces/IPool.sol",
"chars": 406,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IPool {\n function addLiquidity(address "
},
{
"path": "contracts/interfaces/ISigsVerifier.sol",
"chars": 590,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface ISigsVerifier {\n /**\n * @notice Ver"
},
{
"path": "contracts/interfaces/IUniswapV2.sol",
"chars": 542,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IUniswapV2 {\n function swapTokensForExa"
},
{
"path": "contracts/interfaces/IWETH.sol",
"chars": 171,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IWETH {\n function deposit() external pa"
},
{
"path": "contracts/interfaces/IWithdrawInbox.sol",
"chars": 358,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IWithdrawInbox {\n function withdraw(\n "
},
{
"path": "contracts/libraries/BridgeTransferLib.sol",
"chars": 19018,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.s"
},
{
"path": "contracts/libraries/Pb.sol",
"chars": 6602,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\n// runtime proto sol library\nlibrary Pb {\n enum Wi"
},
{
"path": "contracts/libraries/PbBridge.sol",
"chars": 1650,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: bridge.proto\nprag"
},
{
"path": "contracts/libraries/PbFarming.sol",
"chars": 1597,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: contracts/librari"
},
{
"path": "contracts/libraries/PbPegged.sol",
"chars": 2812,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: contracts/librari"
},
{
"path": "contracts/libraries/PbPool.sol",
"chars": 1547,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: contracts/librari"
},
{
"path": "contracts/libraries/PbSgn.sol",
"chars": 1193,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: contracts/librari"
},
{
"path": "contracts/libraries/PbStaking.sol",
"chars": 3362,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: contracts/librari"
},
{
"path": "contracts/libraries/Utils.sol",
"chars": 714,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nlibrary Utils {\n // https://ethereum.stackexchang"
},
{
"path": "contracts/libraries/proto/README.md",
"chars": 1063,
"preview": "# about soltype\n\nDue to pb gen sol only supports soltype field option defined in the same proto file. When we have multi"
},
{
"path": "contracts/libraries/proto/bridge.proto",
"chars": 546,
"preview": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor."
},
{
"path": "contracts/libraries/proto/farming.proto",
"chars": 601,
"preview": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor."
},
{
"path": "contracts/libraries/proto/pegged.proto",
"chars": 2270,
"preview": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor."
},
{
"path": "contracts/libraries/proto/pool.proto",
"chars": 614,
"preview": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor."
},
{
"path": "contracts/libraries/proto/sgn.proto",
"chars": 375,
"preview": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor."
},
{
"path": "contracts/libraries/proto/staking.proto",
"chars": 703,
"preview": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor."
},
{
"path": "contracts/liquidity-bridge/Bridge.sol",
"chars": 7665,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/liquidity-bridge/FarmingRewards.sol",
"chars": 3376,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/liquidity-bridge/Pool.sol",
"chars": 6096,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/liquidity-bridge/Signers.sol",
"chars": 5439,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/utils/cryptography/EC"
},
{
"path": "contracts/liquidity-bridge/WithdrawInbox.sol",
"chars": 3048,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../safeguard/Ownable.sol\";\n\n/**\n * @title A c"
},
{
"path": "contracts/message/CHANGELOG.md",
"chars": 1651,
"preview": "# 0.2.0 (2022-03-28)\n\n## MsgDataTypes\n\n- Most message bridge related data types are now in a separate library `MsgDataTy"
},
{
"path": "contracts/message/README.md",
"chars": 80,
"preview": "[Celer Inter-chain Message (IM) Developer Docs](https://im-docs.celer.network/)\n"
},
{
"path": "contracts/message/apps/RFQ.sol",
"chars": 20080,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/Sa"
},
{
"path": "contracts/message/apps/adapter/MessageReceiverAdapter.sol",
"chars": 3268,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"../../framework/MessageApp.sol\";\nimport \"../"
},
{
"path": "contracts/message/apps/examples/BatchTransfer.sol",
"chars": 6124,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../../framework/MessageApp.sol\";\n\n/** @title "
},
{
"path": "contracts/message/apps/examples/MsgExampleBasic.sol",
"chars": 1242,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"../../framework/MessageApp.sol\";\n\n// A Hello"
},
{
"path": "contracts/message/apps/examples/MsgExampleBasicTransfer.sol",
"chars": 3213,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/Sa"
},
{
"path": "contracts/message/apps/examples/MsgExampleInOrder.sol",
"chars": 2014,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"../../framework/MessageApp.sol\";\n\n// a simpl"
},
{
"path": "contracts/message/apps/examples/MsgTest.sol",
"chars": 5364,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/Saf"
},
{
"path": "contracts/message/apps/examples/TransferSwap.sol",
"chars": 14220,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/Sa"
},
{
"path": "contracts/message/apps/examples/TransferSwapSendBack.sol",
"chars": 3383,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"../../framework/MessageApp.sol\";\n\ninterface "
},
{
"path": "contracts/message/apps/nft-bridge/MCNNFT.sol",
"chars": 2834,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC721/extension"
},
{
"path": "contracts/message/apps/nft-bridge/NFTBridge.sol",
"chars": 14397,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity 0.8.17;\n\nimport \"../../framework/MessageReceiverApp.sol\";\nimpor"
},
{
"path": "contracts/message/apps/nft-bridge/OrigNFT.sol",
"chars": 496,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC721/extension"
},
{
"path": "contracts/message/apps/nft-bridge/PegNFT.sol",
"chars": 779,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC721/extension"
},
{
"path": "contracts/message/framework/MessageApp.sol",
"chars": 289,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"./MessageSenderApp.sol\";\nimport \"./MessageRe"
},
{
"path": "contracts/message/framework/MessageBusAddress.sol",
"chars": 139,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nabstract contract MessageBusAddress {\n address pu"
},
{
"path": "contracts/message/framework/MessageReceiverApp.sol",
"chars": 4526,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"../interfaces/IMessageReceiverApp.sol\";\nimpo"
},
{
"path": "contracts/message/framework/MessageSenderApp.sol",
"chars": 5147,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.s"
},
{
"path": "contracts/message/interfaces/IMessageBus.sol",
"chars": 5634,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"../libraries/MsgDataTypes.sol\";\n\ninterface I"
},
{
"path": "contracts/message/interfaces/IMessageReceiverApp.sol",
"chars": 3899,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IMessageReceiverApp {\n enum ExecutionSt"
},
{
"path": "contracts/message/libraries/MessageSenderLib.sol",
"chars": 8648,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.s"
},
{
"path": "contracts/message/libraries/MsgDataTypes.sol",
"chars": 2650,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nlibrary MsgDataTypes {\n string constant ABORT_PRE"
},
{
"path": "contracts/message/messagebus/MessageBus.sol",
"chars": 1186,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./MessageBusSender.sol\";\nimport \"./MessageBus"
},
{
"path": "contracts/message/messagebus/MessageBusReceiver.sol",
"chars": 24380,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"../libraries/MsgDataTypes.sol\";\nimport \"../i"
},
{
"path": "contracts/message/messagebus/MessageBusSender.sol",
"chars": 5943,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../../safeguard/Ownable.sol\";\nimport \"../../i"
},
{
"path": "contracts/message/safeguard/DelayedMessage.sol",
"chars": 1710,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"../../safeguard/Ownable.sol\";\n\nabstract cont"
},
{
"path": "contracts/message/safeguard/MessageAppPauser.sol",
"chars": 677,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"../libraries/MsgDataTypes.sol\";\nimport \"../."
},
{
"path": "contracts/miscs/Faucet.sol",
"chars": 1539,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n"
},
{
"path": "contracts/miscs/MintableERC20.sol",
"chars": 974,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n"
},
{
"path": "contracts/miscs/oasys/L1StandardERC20.sol",
"chars": 2982,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport "
},
{
"path": "contracts/miscs/oasys/L1StandardERC20Factory.sol",
"chars": 835,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n\nimport {L1StandardERC20} from \"./L1StandardERC20.sol\";\n\n/**\n * "
},
{
"path": "contracts/miscs/oasys/README.md",
"chars": 121,
"preview": "# Oasys specific contracts\n\nUsed for generating typechain for oasys_token_factory.ts to deploy new tokens on Oasys chain"
},
{
"path": "contracts/miscs/proxy-admin/Ownable.sol",
"chars": 2501,
"preview": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \""
},
{
"path": "contracts/miscs/proxy-admin/ProxyAdmin.sol",
"chars": 2934,
"preview": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/transparent/ProxyAdmin.sol)\n\npragma solidity ^0."
},
{
"path": "contracts/pegged-bridge/OriginalTokenVault.sol",
"chars": 8633,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/pegged-bridge/OriginalTokenVaultV2.sol",
"chars": 8990,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/pegged-bridge/PeggedBrc20Bridge.sol",
"chars": 7076,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../interfaces/IPeggedToken.sol\";\nimport \"../i"
},
{
"path": "contracts/pegged-bridge/PeggedTokenBridge.sol",
"chars": 5848,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../interfaces/ISigsVerifier.sol\";\nimport \"../"
},
{
"path": "contracts/pegged-bridge/PeggedTokenBridgeV2.sol",
"chars": 7877,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../interfaces/ISigsVerifier.sol\";\nimport \"../"
},
{
"path": "contracts/pegged-bridge/README.md",
"chars": 2307,
"preview": "# Pegged Token Bridge\n\nGoal: Token T exists on chain A but not on chain B, and we would like to support a 1:1 pegged tok"
},
{
"path": "contracts/pegged-bridge/customized/PeggedNativeTokenBridge.sol",
"chars": 922,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../PeggedTokenBridgeV2.sol\";\n\ninterface INati"
},
{
"path": "contracts/pegged-bridge/tokens/ERC20Permit/MintSwapCanonicalTokenPermit.sol",
"chars": 701,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/extension"
},
{
"path": "contracts/pegged-bridge/tokens/ERC20Permit/MultiBridgeTokenPermit.sol",
"chars": 684,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/extension"
},
{
"path": "contracts/pegged-bridge/tokens/ERC20Permit/SingleBridgeTokenPermit.sol",
"chars": 710,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/extension"
},
{
"path": "contracts/pegged-bridge/tokens/IntermediaryBridgeToken.sol",
"chars": 2147,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.so"
},
{
"path": "contracts/pegged-bridge/tokens/IntermediaryOriginalToken.sol",
"chars": 2021,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.so"
},
{
"path": "contracts/pegged-bridge/tokens/MintSwapCanonicalToken.sol",
"chars": 2566,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/Sa"
},
{
"path": "contracts/pegged-bridge/tokens/MintSwapCanonicalTokenUpgradable.sol",
"chars": 1662,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"./MintSwapCanonicalToken.sol\";\n\n/**\n * @titl"
},
{
"path": "contracts/pegged-bridge/tokens/MultiBridgeToken.sol",
"chars": 3746,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol"
},
{
"path": "contracts/pegged-bridge/tokens/SingleBridgeToken.sol",
"chars": 2882,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol"
},
{
"path": "contracts/pegged-bridge/tokens/SwapBridgeToken.sol",
"chars": 2640,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.so"
},
{
"path": "contracts/pegged-bridge/tokens/WrappedBridgeToken.sol",
"chars": 2375,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.so"
},
{
"path": "contracts/pegged-bridge/tokens/customized/CircleBridgeToken.sol",
"chars": 3414,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.so"
},
{
"path": "contracts/pegged-bridge/tokens/customized/FraxBridgeToken.sol",
"chars": 2402,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.so"
},
{
"path": "contracts/pegged-bridge/tokens/customized/MaiBridgeToken.sol",
"chars": 2625,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.so"
},
{
"path": "contracts/pegged-bridge/tokens/customized/OntologyBridgeToken.sol",
"chars": 2792,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.so"
},
{
"path": "contracts/pegged-bridge/tokens/customized/xc20/XC20BridgeHub.sol",
"chars": 10267,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n"
},
{
"path": "contracts/pegged-bridge/tokens/customized/xc20/XC20BridgeToken.sol",
"chars": 2685,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n"
},
{
"path": "contracts/pegged-bridge/tokens/customized/xc20/interfaces/IXC20BridgeHub.sol",
"chars": 746,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.0;\n\ninterface IXC20BridgeHub {\n /**\n * @dev Swaps "
},
{
"path": "contracts/pegged-bridge/tokens/freezable/Freezable.sol",
"chars": 598,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nabstract contract Freezable {\n event Frozen(addres"
},
{
"path": "contracts/pegged-bridge/tokens/freezable/MintSwapCanonicalTokenFreezable.sol",
"chars": 1185,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Freezable.sol\";\nimport \"../MintSwapCanonica"
},
{
"path": "contracts/pegged-bridge/tokens/freezable/MintSwapCanonicalTokenUpgradableFreezable.sol",
"chars": 1236,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Freezable.sol\";\nimport \"../MintSwapCanonica"
},
{
"path": "contracts/pegged-bridge/tokens/owners/RestrictedMultiBridgeTokenOwner.sol",
"chars": 1283,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../../../safeguard/Ownable.sol\";\n\ninterface I"
},
{
"path": "contracts/proxy/TransferAgent.sol",
"chars": 5683,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/safeguard/DelayedTransfer.sol",
"chars": 2246,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Governor.sol\";\n\nabstract contract DelayedTr"
},
{
"path": "contracts/safeguard/Governor.sol",
"chars": 1264,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Ownable.sol\";\n\nabstract contract Governor i"
},
{
"path": "contracts/safeguard/Ownable.sol",
"chars": 2283,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Contract module which provides a basic ac"
},
{
"path": "contracts/safeguard/Pauser.sol",
"chars": 1400,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/security/Pausable.sol"
},
{
"path": "contracts/safeguard/VolumeControl.sol",
"chars": 1704,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Governor.sol\";\n\nabstract contract VolumeCon"
},
{
"path": "contracts/safeguard/Whitelist.sol",
"chars": 1381,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Ownable.sol\";\n\nabstract contract Whitelist "
},
{
"path": "contracts/safeguard/sentinel/Guard.sol",
"chars": 3742,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../Ownable.sol\";\n\nabstract contract Guard is "
},
{
"path": "contracts/safeguard/sentinel/GuardedGovernor.sol",
"chars": 9249,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Guard.sol\";\n\ninterface IBridge {\n // del"
},
{
"path": "contracts/safeguard/sentinel/GuardedPauser.sol",
"chars": 4057,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Guard.sol\";\nimport \"../../libraries/Utils.s"
},
{
"path": "contracts/safeguard/sentinel/Sentinel.sol",
"chars": 874,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./GuardedPauser.sol\";\nimport \"./GuardedGovern"
},
{
"path": "contracts/staking/DataTypes.sol",
"chars": 2347,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nlibrary DataTypes {\n uint256 constant CELR_DECIMAL"
},
{
"path": "contracts/staking/Govern.sol",
"chars": 5249,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/staking/SGN.sol",
"chars": 3751,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/staking/Staking.sol",
"chars": 31404,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/staking/StakingReward.sol",
"chars": 2596,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.so"
},
{
"path": "contracts/staking/Viewer.sol",
"chars": 4702,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport {DataTypes as dt} from \"./DataTypes.sol\";\nimpo"
},
{
"path": "contracts/test-helpers/DummySwap.sol",
"chars": 1408,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.s"
},
{
"path": "contracts/test-helpers/TestERC20.sol",
"chars": 434,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol"
},
{
"path": "contracts/test-helpers/WETH.sol",
"chars": 523,
"preview": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol"
},
{
"path": "deploy/circle-usdc/000_circle_bridge_proxy.ts",
"chars": 657,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/circle-usdc/000_circle_bridge_proxy_v2.ts",
"chars": 661,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/core/000_sgn_staking.ts",
"chars": 1470,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/governed-owner/000_governed_owner.ts",
"chars": 1309,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst"
},
{
"path": "deploy/governed-owner/customized/000_message_bus_owner.ts",
"chars": 932,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst"
},
{
"path": "deploy/liquidity-bridge/000_bridge.ts",
"chars": 615,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/liquidity-bridge/001_farming_rewards.ts",
"chars": 752,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/message/000_message_bus_init.ts",
"chars": 2453,
"preview": "import * as dotenv from 'dotenv';\nimport { AbiCoder } from 'ethers';\nimport { DeployFunction } from 'hardhat-deploy/type"
},
{
"path": "deploy/message/001_message_bus.ts",
"chars": 1025,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/message/apps/000_transfer_swap.ts",
"chars": 746,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/message/apps/001_nft_bridge.ts",
"chars": 943,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/message/apps/002_peg_nft.ts",
"chars": 645,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/message/apps/003_orig_nft.ts",
"chars": 612,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/message/apps/004_mcn_nft.ts",
"chars": 645,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/message/apps/005_msg_test.ts",
"chars": 512,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst"
},
{
"path": "deploy/message/apps/006_rfq.ts",
"chars": 595,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/message/apps/007_adapter.ts",
"chars": 632,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/miscs/000_test_token.ts",
"chars": 847,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/miscs/001_faucet.ts",
"chars": 455,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst"
},
{
"path": "deploy/miscs/002_dummy_swap.ts",
"chars": 500,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst"
},
{
"path": "deploy/miscs/003_withdraw_inbox.ts",
"chars": 561,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst"
},
{
"path": "deploy/miscs/004_contract_as_lp.ts",
"chars": 527,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst"
},
{
"path": "deploy/miscs/005_contract_as_sender.ts",
"chars": 475,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst"
},
{
"path": "deploy/miscs/006_transfer_agent.ts",
"chars": 539,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/miscs/007_default_proxy_admin.ts",
"chars": 734,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/000_original_token_vault.ts",
"chars": 621,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/001_pegged_token_bridge.ts",
"chars": 618,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/002_original_token_vault_v2.ts",
"chars": 781,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/003_pegged_token_bridge_v2.ts",
"chars": 776,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/004_pegged_brc20_bridge.ts",
"chars": 761,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/customized/000_pegged_native_token_bridge.ts",
"chars": 784,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/000_single_bridge_token.ts",
"chars": 761,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/001_single_bridge_token_permit.ts",
"chars": 801,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/002_multi_bridge_token.ts",
"chars": 848,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/003_multi_bridge_token_permit.ts",
"chars": 743,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/004_mint_swap_canonical_token.ts",
"chars": 894,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/005_mint_swap_canonical_token_permit.ts",
"chars": 762,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/006_mint_swap_canonical_token_upgradable.ts",
"chars": 1152,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/007_wrapped_bridge_token.ts",
"chars": 909,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/008_intermediary_bridge_token.ts",
"chars": 949,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/009_intermediary_original_token.ts",
"chars": 1016,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/010_circle_bridge_token.ts",
"chars": 1125,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/customized/000_frax_bridge_token.ts",
"chars": 886,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/customized/001_mai_bridge_token.ts",
"chars": 738,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/customized/002_ontology_bridge_token.ts",
"chars": 823,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/freezable/000_mint_swap_canonical_token_upgradable_freezable.ts",
"chars": 1145,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/freezable/001_mint_swap_canonical_token_freezable.ts",
"chars": 761,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/pegged-bridge/tokens/owners/000_restricted_multi_bridge_token_owner.ts",
"chars": 728,
"preview": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironm"
},
{
"path": "deploy/sentinel/000_sentinel.ts",
"chars": 2243,
"preview": "import * as dotenv from 'dotenv';\nimport { AbiCoder } from 'ethers';\nimport { DeployFunction } from 'hardhat-deploy/type"
},
{
"path": "deploy/sentinel/001_sentinel_zksync.ts",
"chars": 1164,
"preview": "// Uncomment for zksync\n// import * as dotenv from 'dotenv';\n// import * as hre from 'hardhat';\n// import { Wallet } fro"
},
{
"path": "hardhat.config.ts",
"chars": 24715,
"preview": "import '@nomicfoundation/hardhat-ethers';\nimport '@nomicfoundation/hardhat-toolbox';\nimport '@nomicfoundation/hardhat-ve"
}
]
// ... and 36 more files (download for full content)
About this extraction
This page contains the full source code of the celer-network/sgn-v2-contracts GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 236 files (794.0 KB), approximately 205.6k tokens, and a symbol index with 130 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.