├── apps ├── bridge-evm │ ├── deployments │ │ ├── bsc │ │ │ └── .chainId │ │ ├── ethereum │ │ │ └── .chainId │ │ ├── fuji │ │ │ └── .chainId │ │ ├── goerli │ │ │ └── .chainId │ │ ├── optimism │ │ │ └── .chainId │ │ ├── polygon │ │ │ └── .chainId │ │ ├── arbitrum │ │ │ └── .chainId │ │ ├── avalanche │ │ │ └── .chainId │ │ └── goerli-sandbox │ │ │ └── .chainId │ ├── .prettierignore │ ├── config │ │ ├── .solhintignore │ │ ├── .solcover.js │ │ └── .solhint.json │ ├── slither.config.json │ ├── README.md │ ├── foundry.toml │ ├── contracts │ │ ├── interfaces │ │ │ ├── IWETH.sol │ │ │ └── ITokenBridge.sol │ │ └── mocks │ │ │ ├── Token.sol │ │ │ └── WETH.sol │ ├── tasks │ │ ├── testBridge.ts │ │ ├── config │ │ │ ├── addresses │ │ │ │ ├── sandboxAddresses.ts │ │ │ │ ├── testnetAddresses.ts │ │ │ │ ├── index.ts │ │ │ │ └── mainnetAddresses.ts │ │ │ ├── index.ts │ │ │ ├── sandbox.ts │ │ │ ├── types.ts │ │ │ ├── testnet.ts │ │ │ └── mainnet.ts │ │ ├── mint.ts │ │ ├── deployBridge.ts │ │ ├── index.ts │ │ ├── utils │ │ │ └── readStatic.ts │ │ └── send.ts │ ├── deploy │ │ ├── WETH.ts │ │ ├── Token.ts │ │ └── TokenBridge.ts │ ├── .prettierrc.js │ ├── hardhat-coverage.config.ts │ ├── test │ │ └── foundry │ │ │ └── TokenBridgeProxy.sol │ ├── package.json │ ├── hardhat.config.ts │ └── LICENSE ├── bridge │ ├── Move.toml │ └── sources │ │ └── limiter.move ├── oracle │ └── Move.toml ├── counter │ └── Move.toml ├── example │ ├── oft │ │ └── Move.toml │ └── proxy-oft │ │ └── Move.toml └── README.md ├── sdk ├── .prettierignore ├── babel.config.json ├── src │ ├── modules │ │ ├── uln │ │ │ ├── msglib_v1_0.ts │ │ │ ├── index.ts │ │ │ ├── packet_event.ts │ │ │ └── uln_signer.ts │ │ ├── index.ts │ │ ├── msglib_auth.ts │ │ ├── executor_config.ts │ │ ├── apps │ │ │ ├── coin.ts │ │ │ └── counter.ts │ │ ├── msglib_config.ts │ │ ├── endpoint.ts │ │ └── channel.ts │ ├── types │ │ └── index.ts │ ├── constants.ts │ └── utils.test.ts ├── jest.config.json ├── tasks │ ├── deploy │ │ ├── index.ts │ │ ├── deployZro.ts │ │ ├── executor │ │ │ └── deployExecutorV2.ts │ │ ├── msglib │ │ │ ├── deployMsglibV2.ts │ │ │ └── deployMsglibV1_1.ts │ │ ├── deployBridge.ts │ │ ├── deployCounter.ts │ │ ├── deployLayerzero.ts │ │ ├── deployOracle.ts │ │ └── deployCommon.ts │ ├── config │ │ ├── index.ts │ │ ├── common.ts │ │ ├── local.ts │ │ └── sandbox.ts │ ├── options.ts │ ├── index.ts │ └── utils.ts ├── tests │ ├── utils.ts │ └── bcs.test.ts ├── .eslintrc.json ├── .prettierrc.js └── package.json ├── zro ├── sources │ └── zro.move └── Move.toml ├── .gitmodules ├── audit └── ottersec-layerzero-aptos-oct-19-2022.pdf ├── layerzero-apps └── Move.toml ├── msglib ├── msglib-auth │ ├── Move.toml │ └── sources │ │ └── msglib_cap.move ├── msglib-v2 │ ├── Move.toml │ └── sources │ │ └── msglib_v2_router.move └── msglib-v1 │ └── msglib-v1-1 │ ├── Move.toml │ └── sources │ ├── msglib_v1_1.move │ └── msglib_v1_1_router.move ├── executor ├── executor-auth │ ├── Move.toml │ └── sources │ │ └── executor_cap.move ├── executor-ext │ ├── Move.toml │ └── sources │ │ └── executor_ext.move └── executor-v2 │ ├── Move.toml │ └── sources │ └── executor_v2.move ├── Makefile.config ├── layerzero-common ├── Move.toml └── sources │ ├── semver.move │ ├── utils.move │ ├── serde.move │ └── acl.move ├── layerzero ├── Move.toml └── sources │ ├── executor_router.move │ ├── msglib-v1 │ ├── v1-0 │ │ ├── packet_event.move │ │ └── uln_signer.move │ └── msglib_router.move │ ├── admin.move │ ├── app │ ├── lzapp │ │ └── remote.move │ └── test_helpers.move │ └── bulletin.move ├── README.md ├── .gitignore └── LICENSE /apps/bridge-evm/deployments/bsc/.chainId: -------------------------------------------------------------------------------- 1 | 56 -------------------------------------------------------------------------------- /sdk/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | tsconfig.json -------------------------------------------------------------------------------- /apps/bridge-evm/deployments/ethereum/.chainId: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /apps/bridge-evm/deployments/fuji/.chainId: -------------------------------------------------------------------------------- 1 | 43113 -------------------------------------------------------------------------------- /apps/bridge-evm/deployments/goerli/.chainId: -------------------------------------------------------------------------------- 1 | 5 -------------------------------------------------------------------------------- /apps/bridge-evm/deployments/optimism/.chainId: -------------------------------------------------------------------------------- 1 | 10 -------------------------------------------------------------------------------- /apps/bridge-evm/deployments/polygon/.chainId: -------------------------------------------------------------------------------- 1 | 137 -------------------------------------------------------------------------------- /apps/bridge-evm/deployments/arbitrum/.chainId: -------------------------------------------------------------------------------- 1 | 42161 -------------------------------------------------------------------------------- /apps/bridge-evm/deployments/avalanche/.chainId: -------------------------------------------------------------------------------- 1 | 43114 -------------------------------------------------------------------------------- /apps/bridge-evm/deployments/goerli-sandbox/.chainId: -------------------------------------------------------------------------------- 1 | 5 -------------------------------------------------------------------------------- /apps/bridge-evm/.prettierignore: -------------------------------------------------------------------------------- 1 | **/lib/**/* 2 | **/coverage/* -------------------------------------------------------------------------------- /zro/sources/zro.move: -------------------------------------------------------------------------------- 1 | module zro::zro { 2 | struct ZRO {} 3 | } -------------------------------------------------------------------------------- /apps/bridge-evm/config/.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | contracts/test/ 4 | 5 | test/ 6 | lib/ -------------------------------------------------------------------------------- /apps/bridge-evm/slither.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "filter_paths": "lib", 3 | "solc_remaps": [ 4 | "forge-std/=lib/forge-std/src/" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "apps/bridge-evm/lib/forge-std"] 2 | path = apps/bridge-evm/lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | -------------------------------------------------------------------------------- /apps/bridge-evm/README.md: -------------------------------------------------------------------------------- 1 | ```bash 2 | # prepare 3 | forge install 4 | yarn test:forge 5 | 6 | # static analysis 7 | slither . 8 | ``` 9 | 10 | -------------------------------------------------------------------------------- /audit/ottersec-layerzero-aptos-oct-19-2022.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Farichaa/LayerZero-Aptos-Contract/HEAD/audit/ottersec-layerzero-aptos-oct-19-2022.pdf -------------------------------------------------------------------------------- /apps/bridge/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bridge" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | bridge = "_" 7 | 8 | [dependencies] 9 | layerzero = { local = "../../layerzero" } -------------------------------------------------------------------------------- /apps/oracle/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "oracle" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | oracle = "_" 7 | 8 | [dependencies] 9 | layerzero = { local = "../../layerzero" } -------------------------------------------------------------------------------- /apps/counter/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "counter" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | counter = "_" 7 | 8 | [dependencies] 9 | layerzero = { local = "../../layerzero" } 10 | -------------------------------------------------------------------------------- /layerzero-apps/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "layerzero_apps" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | layerzero_apps = "_" 7 | 8 | [dependencies] 9 | layerzero = { local = "../layerzero" } -------------------------------------------------------------------------------- /apps/example/oft/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "oft" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | oft = "_" 7 | 8 | [dependencies] 9 | layerzero_apps = { local = "../../../layerzero-apps" } 10 | -------------------------------------------------------------------------------- /msglib/msglib-auth/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "msglib_auth" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | msglib_auth = "_" 7 | 8 | [dependencies] 9 | layerzero_common = { local = "../../layerzero-common" } -------------------------------------------------------------------------------- /executor/executor-auth/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "executor_auth" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | executor_auth = "_" 7 | 8 | [dependencies] 9 | layerzero_common = { local = "../../layerzero-common" } -------------------------------------------------------------------------------- /apps/example/proxy-oft/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "proxy_oft" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | proxy_oft = "_" 7 | 8 | [dependencies] 9 | layerzero_apps = { local = "../../../layerzero-apps" } 10 | -------------------------------------------------------------------------------- /sdk/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "node": "current" 8 | } 9 | } 10 | ], 11 | "@babel/preset-typescript" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /Makefile.config: -------------------------------------------------------------------------------- 1 | layerzero_common=0x10 2 | msglib_auth=0x11 3 | zro=0x12 4 | msglib_v2=0x13 5 | executor_auth=0x15 6 | layerzero=0x14 7 | executor_v2=0x16 8 | msglib_v1_1=0x17 9 | layerzero_apps=0x18 10 | executor_ext=0x19 11 | included_artifacts="none" -------------------------------------------------------------------------------- /executor/executor-ext/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "executor_ext" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | executor_ext = "_" 7 | 8 | [dependencies] 9 | AptosFramework = { local = "../../deps/aptos-core/aptos-move/framework/aptos-framework" } 10 | -------------------------------------------------------------------------------- /executor/executor-v2/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "executor_v2" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | executor_v2 = "_" 7 | 8 | [dependencies] 9 | layerzero_common = { local = "../../layerzero-common" } 10 | executor_auth = { local = "../executor-auth" } -------------------------------------------------------------------------------- /sdk/src/modules/uln/msglib_v1_0.ts: -------------------------------------------------------------------------------- 1 | import { SDK } from "../../index" 2 | 3 | export class MsgLibV1_0 { 4 | public readonly module 5 | 6 | constructor(private sdk: SDK) { 7 | this.module = `${sdk.accounts.layerzero}::msglib_v1_0` 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sdk/jest.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "ts-jest", 3 | "testEnvironment": "node", 4 | "transform": { 5 | "\\.[jt]sx?$": "babel-jest" 6 | }, 7 | "transformIgnorePatterns": [ 8 | "/node_modules/(?!(globby|slash)/)" 9 | ], 10 | "testTimeout": 120000 11 | } 12 | -------------------------------------------------------------------------------- /msglib/msglib-v2/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "msglib_v2" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | msglib_v2 = "_" 7 | 8 | [dependencies] 9 | layerzero_common = { local = "../../layerzero-common" } 10 | msglib_auth = { local = "../msglib-auth" } 11 | zro = { local = "../../zro" } -------------------------------------------------------------------------------- /zro/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zro" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | zro = "_" 7 | 8 | [dependencies.MoveStdlib] 9 | git = "https://github.com/aptos-labs/aptos-core.git" 10 | subdir = "aptos-move/framework/move-stdlib" 11 | rev = "895ae28e4a105430d8b9d2ce2f6e48c3b2c7fcf0" -------------------------------------------------------------------------------- /msglib/msglib-v1/msglib-v1-1/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "msglib_v1_1" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | msglib_v1_1 = "_" 7 | 8 | [dependencies] 9 | layerzero_common = { local = "../../../layerzero-common" } 10 | msglib_auth = { local = "../../msglib-auth" } 11 | zro = { local = "../../../zro" } -------------------------------------------------------------------------------- /layerzero-common/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "layerzero_common" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | layerzero_common = "_" 7 | 8 | [dependencies.AptosFramework] 9 | git = "https://github.com/aptos-labs/aptos-core.git" 10 | subdir = "aptos-move/framework/aptos-framework" 11 | rev = "895ae28e4a105430d8b9d2ce2f6e48c3b2c7fcf0" -------------------------------------------------------------------------------- /apps/bridge-evm/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | solc = '0.8.15' 3 | src = 'contracts' 4 | out = 'out' 5 | libs = ["node_modules", "lib"] 6 | test = 'test/foundry' 7 | remappings = [ 8 | 'forge-std=lib/forge-std/src/', 9 | 'contracts/=contracts/', 10 | ] 11 | 12 | # See more config options https://github.com/gakonst/foundry/tree/master/config -------------------------------------------------------------------------------- /sdk/tasks/deploy/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./deployBridge" 2 | export * from "./deployLayerZero" 3 | export * from "./deployOracle" 4 | export * from "./deployCounter" 5 | export * from "./deployZro" 6 | export * from "./deployCommon" 7 | export * from "./msglib/deployMsglibV1_1" 8 | export * from "./msglib/deployMsglibV2" 9 | export * from "./executor/deployExecutorV2" 10 | -------------------------------------------------------------------------------- /apps/bridge-evm/contracts/interfaces/IWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | 3 | pragma solidity >=0.5.0; 4 | 5 | interface IWETH { 6 | function deposit() external payable; 7 | 8 | function transfer(address to, uint value) external returns (bool); 9 | 10 | function withdraw(uint) external; 11 | 12 | function approve(address to, uint value) external returns (bool); 13 | } 14 | -------------------------------------------------------------------------------- /sdk/tests/utils.ts: -------------------------------------------------------------------------------- 1 | import * as nacl from "tweetnacl" 2 | import * as aptos from "aptos" 3 | 4 | export function findSecretKeyWithZeroPrefix(length = 0): Uint8Array { 5 | const prefix = Buffer.alloc(length, "0").toString() 6 | let address 7 | let secretKey 8 | do { 9 | secretKey = nacl.box.keyPair().secretKey 10 | const account = new aptos.AptosAccount(secretKey) 11 | address = account.address().noPrefix() 12 | } while (!address.startsWith(prefix)) 13 | return secretKey 14 | } 15 | -------------------------------------------------------------------------------- /sdk/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | "prettier" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaVersion": "latest", 14 | "sourceType": "module" 15 | }, 16 | "plugins": [ 17 | "@typescript-eslint", 18 | "unused-imports" 19 | ], 20 | "rules": { 21 | "unused-imports/no-unused-imports": "error" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /layerzero/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "layerzero" 3 | version = "0.0.1" 4 | 5 | [addresses] 6 | layerzero = "_" 7 | test = "0x8" 8 | lzapp = "0x9" 9 | 10 | [dependencies] 11 | layerzero_common = { local = "../layerzero-common" } 12 | zro = { local = "../zro" } 13 | msglib_auth = { local = "../msglib/msglib-auth" } 14 | msglib_v2 = { local = "../msglib/msglib-v2" } 15 | msglib_v1_1 = { local = "../msglib/msglib-v1/msglib-v1-1" } 16 | executor_auth = { local = "../executor/executor-auth" } 17 | executor_v2 = { local = "../executor/executor-v2" } -------------------------------------------------------------------------------- /apps/bridge-evm/config/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: [ 3 | "interfaces/ILayerZeroEndpoint.sol", 4 | "interfaces/ILayerZeroReceiver.sol", 5 | "interfaces/ILayerZeroUserApplicationConfig.sol", 6 | "lzApp/LzApp.sol", 7 | "lzApp/NonblockingLzApp.sol", 8 | "mocks/LZEndpointMock.sol", 9 | ], 10 | configureYulOptimizer: true, 11 | solcOptimizerDetails: { 12 | yul: true, 13 | yulDetails: { 14 | stackAllocation: true, 15 | }, 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /apps/bridge-evm/config/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:all", 3 | "rules": { 4 | "compiler-version": ["error", ">=0.8.7"], 5 | "func-visibility": ["warn", { "ignoreConstructors": true }], 6 | "no-empty-blocks": "off", 7 | "no-inline-assembly": "off", 8 | "avoid-low-level-calls": "off", 9 | "not-rely-on-time": "off", 10 | "var-name-mixedcase": "off", 11 | "func-name-mixedcase": "off", 12 | "max-line-length": ["warn", 80], 13 | "function-max-lines": "off", 14 | "code-complexity": ["warn", 15] 15 | } 16 | } -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/testBridge.ts: -------------------------------------------------------------------------------- 1 | import * as crossChainHelper from "./utils/crossChainHelper" 2 | 3 | const SRC_NETWORKS = ["ethereum-fork"] 4 | 5 | module.exports = async function (taskArgs, hre) { 6 | // contracts already deployed 7 | crossChainHelper.setForking(true, ["TokenBridge"]) 8 | 9 | await hre.run("deployBridge", { 10 | networks: SRC_NETWORKS.join(","), 11 | deleteOldDeploy: true, 12 | }) 13 | 14 | await hre.run("wireAllSubtask", { 15 | e: "mainnet", 16 | srcNetworks: SRC_NETWORKS.map((n) => n.replace("-fork", "")).join(","), 17 | noPrompt: true, 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/config/addresses/sandboxAddresses.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@layerzerolabs/core-sdk" 2 | 3 | type SandboxChainId = ChainId.GOERLI_SANDBOX | ChainId.ARBITRUM_GOERLI_SANDBOX | ChainId.OPTIMISM_GOERLI_SANDBOX 4 | 5 | export const WETH_ADDRESS: { [chainId in SandboxChainId]?: string } = { 6 | [ChainId.GOERLI_SANDBOX]: "0xcC0235a403E77C56d0F271054Ad8bD3ABcd21904", 7 | } 8 | 9 | export const USDT_ADDRESS: { [chainId in SandboxChainId]?: string } = {} 10 | 11 | export const USDC_ADDRESS: { [chainId in SandboxChainId]?: string } = { 12 | [ChainId.GOERLI_SANDBOX]: "0x30c212b53714daf3739Ff607AaA8A0A18956f13c", 13 | } 14 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/config/index.ts: -------------------------------------------------------------------------------- 1 | import { ChainStage } from "@layerzerolabs/core-sdk" 2 | import { CONFIG as SandboxConfig } from "./sandbox" 3 | import { CONFIG as TestnetConfig } from "./testnet" 4 | import { CONFIG as MainnetConfig } from "./mainnet" 5 | 6 | export function getConfig(stage: ChainStage) { 7 | switch (stage) { 8 | case ChainStage.TESTNET_SANDBOX: 9 | return SandboxConfig 10 | case ChainStage.TESTNET: 11 | return TestnetConfig 12 | case ChainStage.MAINNET: 13 | return MainnetConfig 14 | default: 15 | throw new Error(`Invalid stage: ${stage}`) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/mint.ts: -------------------------------------------------------------------------------- 1 | import { CoinType } from "../../../sdk/src/modules/apps/coin" 2 | 3 | module.exports = async function (taskArgs, hre) { 4 | const receiver = taskArgs.a 5 | switch (taskArgs.t) { 6 | case CoinType.USDC: { 7 | const usdc = await hre.ethers.getContract("Token") 8 | 9 | await (await usdc.mint(receiver, "5000000000000000000")).wait() 10 | const balance = await usdc.balanceOf(receiver) 11 | console.log(`USDC balance of ${receiver}: ${balance.toString()}`) 12 | break 13 | } 14 | default: { 15 | throw new Error(`Not mintable token type: ${taskArgs.t}`) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /apps/bridge-evm/contracts/mocks/Token.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | 3 | pragma solidity ^0.8.12; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract Token is ERC20 { 8 | uint8 DECIMALS; 9 | 10 | constructor( 11 | string memory _name, 12 | string memory _symbol, 13 | uint8 _decimals 14 | ) ERC20(_name, _symbol) { 15 | DECIMALS = _decimals; 16 | _mint(msg.sender, 10000 * 10**DECIMALS); 17 | } 18 | 19 | function mint(address _account, uint _amount) public { 20 | _mint(_account, _amount); 21 | } 22 | 23 | function decimals() public view override returns (uint8) { 24 | return DECIMALS; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/config/sandbox.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@layerzerolabs/core-sdk" 2 | import { CoinType } from "../../../../sdk/src/modules/apps/coin" 3 | import { PacketType } from "./types" 4 | 5 | export const CONFIG = { 6 | useCustomAdapterParams: true, 7 | feeBP: 6, 8 | minDstGas: { 9 | [PacketType.SEND_TO_APTOS]: 2500, 10 | }, 11 | coins: { 12 | [ChainId.GOERLI_SANDBOX]: [CoinType.WETH, CoinType.USDC], 13 | }, 14 | // appConfig: { 15 | // [ChainId.GOERLI_SANDBOX]: { 16 | // [UlnV2ConfigType.OUTBOUND_PROOF_TYPE]: 2, 17 | // [UlnV2ConfigType.INBOUND_PROOF_LIBRARY_VERSION]: 1, //inbound proof library on evm from aptos 18 | // }, 19 | // }, 20 | } 21 | -------------------------------------------------------------------------------- /sdk/src/modules/uln/index.ts: -------------------------------------------------------------------------------- 1 | import { SDK } from "../../index" 2 | import { UlnReceive } from "./uln_receive" 3 | import { UlnSigner } from "./uln_signer" 4 | import { UlnConfig } from "./uln_config" 5 | import { MsgLibV1_0 } from "./msglib_v1_0" 6 | import { PacketEvent } from "./packet_event" 7 | 8 | export class Uln { 9 | Receive: UlnReceive 10 | Signer: UlnSigner 11 | Config: UlnConfig 12 | MsgLibV1: MsgLibV1_0 13 | PacketEvent: PacketEvent 14 | 15 | constructor(sdk: SDK) { 16 | this.Receive = new UlnReceive(sdk) 17 | this.Signer = new UlnSigner(sdk) 18 | this.Config = new UlnConfig(sdk) 19 | this.MsgLibV1 = new MsgLibV1_0(sdk) 20 | this.PacketEvent = new PacketEvent(sdk) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/config/types.ts: -------------------------------------------------------------------------------- 1 | export const enum PacketType { 2 | SEND_TO_APTOS, 3 | RECEIVE_FROM_APTOS, 4 | } 5 | 6 | export const enum UlnV2ConfigType { 7 | INBOUND_PROOF_LIBRARY_VERSION = 1, 8 | INBOUND_BLOCK_CONFIRMATIONS, 9 | RELAYER, 10 | OUTBOUND_PROOF_TYPE, 11 | OUTBOUND_BLOCK_CONFIRMATIONS, 12 | ORACLE, 13 | } 14 | 15 | export const ULNV2_CONFIG_TYPE_LOOKUP = { 16 | [UlnV2ConfigType.INBOUND_PROOF_LIBRARY_VERSION]: "uint16", 17 | [UlnV2ConfigType.INBOUND_BLOCK_CONFIRMATIONS]: "uint64", 18 | [UlnV2ConfigType.RELAYER]: "address", 19 | [UlnV2ConfigType.OUTBOUND_PROOF_TYPE]: "uint16", 20 | [UlnV2ConfigType.OUTBOUND_BLOCK_CONFIRMATIONS]: "uint64", 21 | [UlnV2ConfigType.ORACLE]: "address", 22 | } 23 | -------------------------------------------------------------------------------- /sdk/tasks/config/index.ts: -------------------------------------------------------------------------------- 1 | import { ChainId, ChainStage } from "@layerzerolabs/core-sdk" 2 | import { getConfig as SandboxConfig } from "./sandbox" 3 | import { getConfig as TestnetConfig } from "./testnet" 4 | import { getConfig as MainnetConfig } from "./mainnet" 5 | 6 | export * from "./common" 7 | 8 | export function LzConfig(stage: ChainStage, chainIds: ChainId[], forked = false) { 9 | switch (stage) { 10 | case ChainStage.TESTNET_SANDBOX: 11 | return SandboxConfig() 12 | case ChainStage.TESTNET: 13 | return TestnetConfig(chainIds) 14 | case ChainStage.MAINNET: 15 | return MainnetConfig(chainIds, forked) 16 | default: 17 | throw new Error(`Invalid stage: ${stage}`) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LayerZero Aptos 2 | 3 | LayerZero Aptos endpoint. 4 | 5 | ## Development Guide 6 | 7 | - [Building on LayerZero](apps/README.md) 8 | 9 | ## Setup 10 | 11 | ```shell 12 | git submodule init 13 | git submodule update --recursive 14 | 15 | cargo install --path deps/aptos-core/crates/aptos 16 | ``` 17 | 18 | ## Running tests 19 | 20 | ### move modules 21 | 22 | run tests of move modules 23 | 24 | ```shell 25 | make test 26 | ``` 27 | 28 | ### SDK 29 | 30 | to run tests of SDK, we need to launch local testnet first, 31 | 32 | ```shell 33 | aptos node run-local-testnet --force-restart --assume-yes --with-faucet 34 | ``` 35 | 36 | then execute tests 37 | ```shell 38 | cd sdk 39 | npx jest ./tests/omniCounter.test.ts 40 | npx jest ./tests/bridge.test.ts 41 | ``` 42 | -------------------------------------------------------------------------------- /apps/bridge-evm/deploy/WETH.ts: -------------------------------------------------------------------------------- 1 | import { CHAIN_STAGE, ChainStage } from "@layerzerolabs/core-sdk" 2 | 3 | module.exports = async function ({ deployments, getNamedAccounts, network }) { 4 | const { deploy } = deployments 5 | const { deployer } = await getNamedAccounts() 6 | 7 | const name = network.name.replace("-fork", "") 8 | if (CHAIN_STAGE[name] !== ChainStage.TESTNET || network.name.includes("fork")) { 9 | throw new Error("Only supported on testnet / fork") 10 | } 11 | 12 | console.log(`deployer: ${deployer}`) 13 | 14 | await deploy("WETH", { 15 | from: deployer, 16 | args: [], 17 | log: true, 18 | waitConfirmations: 1, 19 | skipIfAlreadyDeployed: true, 20 | }) 21 | } 22 | 23 | module.exports.tags = ["WETH"] 24 | -------------------------------------------------------------------------------- /apps/bridge-evm/deploy/Token.ts: -------------------------------------------------------------------------------- 1 | import { CHAIN_STAGE, ChainStage } from "@layerzerolabs/core-sdk" 2 | 3 | module.exports = async function ({ deployments, getNamedAccounts, network }) { 4 | const { deploy } = deployments 5 | const { deployer } = await getNamedAccounts() 6 | 7 | console.log(`Network: ${network.name}`) 8 | 9 | const name = network.name.replace("-fork", "") 10 | if (CHAIN_STAGE[name] !== ChainStage.TESTNET || network.name.includes("fork")) { 11 | throw new Error("Only supported on testnet / fork") 12 | } 13 | 14 | await deploy("Token", { 15 | from: deployer, 16 | args: ["USDC", "USDC", 6], 17 | log: true, 18 | waitConfirmations: 1, 19 | skipIfAlreadyDeployed: true, 20 | }) 21 | } 22 | 23 | module.exports.tags = ["Token"] 24 | -------------------------------------------------------------------------------- /executor/executor-v2/sources/executor_v2.move: -------------------------------------------------------------------------------- 1 | module executor_v2::executor_v2 { 2 | use aptos_framework::coin::Coin; 3 | use aptos_framework::aptos_coin::AptosCoin; 4 | use layerzero_common::packet::Packet; 5 | use executor_auth::executor_cap::ExecutorCapability; 6 | 7 | const ELAYERZERO_NOT_SUPPORTED: u64 = 0x00; 8 | 9 | public fun request( 10 | _executor: address, 11 | _packet: &Packet, 12 | _adapter_params: vector, 13 | _fee: Coin, 14 | _cap: &ExecutorCapability 15 | ): Coin { 16 | abort ELAYERZERO_NOT_SUPPORTED 17 | } 18 | 19 | public fun quote_fee(_ua_address: address, _executor: address, _dst_chain_id: u64, _adapter_params: vector): u64 { 20 | abort ELAYERZERO_NOT_SUPPORTED 21 | } 22 | } -------------------------------------------------------------------------------- /sdk/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import * as aptos from "aptos" 2 | 3 | export interface TypeInfo { 4 | account_address: string 5 | module_name: string 6 | struct_name: string 7 | } 8 | 9 | export interface TypeInfoEx extends TypeInfo { 10 | type: string 11 | } 12 | 13 | export interface UlnConfigType { 14 | inbound_confirmations: aptos.BCS.Uint64 15 | oracle: string 16 | outbound_confirmations: aptos.BCS.Uint64 17 | relayer: string 18 | } 19 | 20 | export interface Packet { 21 | nonce: string | aptos.BCS.Uint64 22 | src_chain_id: string | aptos.BCS.Uint16 23 | src_address: Buffer 24 | dst_chain_id: string | aptos.BCS.Uint16 25 | dst_address: Buffer 26 | payload: Buffer 27 | } 28 | 29 | export interface UlnSignerFee { 30 | base_fee: aptos.BCS.Uint64 31 | fee_per_byte: aptos.BCS.Uint64 32 | } 33 | 34 | export enum Environment { 35 | MAINNET = "mainnet", 36 | TESTNET = "testnet", 37 | DEVNET = "devnet", 38 | LOCAL = "local", 39 | } 40 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/config/testnet.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@layerzerolabs/core-sdk" 2 | import { CoinType } from "../../../../sdk/src/modules/apps/coin" 3 | import { PacketType } from "./types" 4 | import { getConfig } from "../../../../sdk/tasks/config/testnet" 5 | 6 | export const CONFIG = { 7 | useCustomAdapterParams: true, 8 | feeBP: 6, 9 | minDstGas: { 10 | [PacketType.SEND_TO_APTOS]: 2500, 11 | }, 12 | coins: { 13 | [ChainId.GOERLI]: getCoinsFromAptosConfig(ChainId.GOERLI), 14 | [ChainId.FUJI]: getCoinsFromAptosConfig(ChainId.FUJI), 15 | }, 16 | } 17 | 18 | function getCoinsFromAptosConfig(chainId: ChainId): CoinType[] { 19 | const types = [] 20 | const coins = getConfig([]).bridge.coins 21 | for (const coin in coins) { 22 | const remotes = Object.keys(coins[coin].remotes) 23 | if (remotes.includes(chainId.toString())) { 24 | types.push(coin as CoinType) 25 | } 26 | } 27 | return types 28 | } 29 | -------------------------------------------------------------------------------- /apps/bridge-evm/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | overrides: [ 3 | { 4 | files: "*.sol", 5 | options: { 6 | bracketSpacing: false, 7 | printWidth: 100, 8 | tabWidth: 4, 9 | useTabs: false, 10 | singleQuote: false, 11 | explicitTypes: "never", 12 | }, 13 | }, 14 | { 15 | files: "*.ts", 16 | options: { 17 | printWidth: 145, 18 | tabWidth: 4, 19 | useTabs: false, 20 | semi: false, 21 | trailingComma: "es5", 22 | }, 23 | }, 24 | { 25 | files: "*.js", 26 | options: { 27 | printWidth: 145, 28 | tabWidth: 4, 29 | useTabs: false, 30 | semi: false, 31 | trailingComma: "es5", 32 | }, 33 | }, 34 | ], 35 | } 36 | -------------------------------------------------------------------------------- /sdk/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | overrides: [ 3 | { 4 | files: "*.move", 5 | options: { 6 | bracketSpacing: false, 7 | printWidth: 300, 8 | tabWidth: 4, 9 | useTabs: false, 10 | singleQuote: false, 11 | explicitTypes: "never", 12 | }, 13 | }, 14 | { 15 | files: ["*.ts", "*.mts"], 16 | options: { 17 | printWidth: 120, 18 | semi: false, 19 | tabWidth: 4, 20 | useTabs: false, 21 | singleQuote: true, 22 | trailingComma: "es5", 23 | }, 24 | }, 25 | { 26 | files: "*.js", 27 | options: { 28 | printWidth: 120, 29 | semi: false, 30 | tabWidth: 4, 31 | useTabs: false, 32 | trailingComma: "es5", 33 | }, 34 | }, 35 | ], 36 | } 37 | -------------------------------------------------------------------------------- /apps/bridge-evm/hardhat-coverage.config.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv" 2 | 3 | import { HardhatUserConfig } from "hardhat/config" 4 | import "@nomiclabs/hardhat-waffle" 5 | import "hardhat-gas-reporter" 6 | import "solidity-coverage" 7 | 8 | dotenv.config() 9 | 10 | // You need to export an object to set up your config 11 | // Go to https://hardhat.org/config/ to learn more 12 | 13 | const config: HardhatUserConfig = { 14 | solidity: { 15 | compilers: [ 16 | { 17 | version: "0.8.15", 18 | settings: { 19 | viaIR: false, 20 | optimizer: { 21 | enabled: false, 22 | }, 23 | }, 24 | }, 25 | ], 26 | }, 27 | networks: { 28 | hardhat: { 29 | blockGasLimit: 30_000_000, 30 | throwOnCallFailures: false, 31 | }, 32 | }, 33 | gasReporter: { 34 | enabled: process.env.REPORT_GAS !== undefined, 35 | currency: "USD", 36 | }, 37 | } 38 | 39 | export default config 40 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/deployBridge.ts: -------------------------------------------------------------------------------- 1 | import * as crossChainHelper from "./utils/crossChainHelper" 2 | import { promises as fs } from "fs" 3 | import * as util from "util" 4 | import * as child_process from "child_process" 5 | 6 | module.exports = async function (taskArgs, hre) { 7 | const networks = taskArgs.networks.split(",") 8 | 9 | const command = "npx hardhat compile" 10 | const execPromise = util.promisify(child_process.exec) 11 | await execPromise(command) 12 | 13 | if (taskArgs.deleteOldDeploy) { 14 | await Promise.all( 15 | networks.map(async (network) => { 16 | const file = `./deployments/${network}/TokenBridge.json` 17 | try { 18 | await fs.rm(file) 19 | } catch { 20 | console.log(`No file to delete: ${file}`) 21 | } 22 | }) 23 | ) 24 | } 25 | 26 | await Promise.all( 27 | networks.map(async (network) => { 28 | await crossChainHelper.deployContract(hre, network, ["TokenBridge"]) 29 | }) 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /sdk/src/modules/index.ts: -------------------------------------------------------------------------------- 1 | import { SDK } from "../index" 2 | import { Channel } from "./channel" 3 | import { Endpoint } from "./endpoint" 4 | import { Executor } from "./executor" 5 | import { Uln } from "./uln" 6 | import { MsgLibConfig } from "./msglib_config" 7 | import { ExecutorConfig } from "./executor_config" 8 | import { MsgLibAuth } from "./msglib_auth" 9 | 10 | export * as counter from "./apps/counter" 11 | export * as bridge from "./apps/bridge" 12 | export * as coin from "./apps/coin" 13 | export * as oracle from "./apps/oracle" 14 | 15 | export class Layerzero { 16 | Channel: Channel 17 | Executor: Executor 18 | Endpoint: Endpoint 19 | Uln: Uln 20 | MsgLibConfig: MsgLibConfig 21 | MsgLibAuth: MsgLibAuth 22 | ExecutorConfig: ExecutorConfig 23 | 24 | constructor(sdk: SDK) { 25 | this.Channel = new Channel(sdk) 26 | this.Executor = new Executor(sdk) 27 | this.Endpoint = new Endpoint(sdk) 28 | this.MsgLibConfig = new MsgLibConfig(sdk) 29 | this.MsgLibAuth = new MsgLibAuth(sdk) 30 | this.ExecutorConfig = new ExecutorConfig(sdk) 31 | this.Uln = new Uln(sdk) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /sdk/tasks/deploy/deployZro.ts: -------------------------------------------------------------------------------- 1 | import { FAUCET_URL, NODE_URL } from "../../src/constants" 2 | import * as aptos from "aptos" 3 | import * as layerzero from "../../src" 4 | import * as path from "path" 5 | import { compilePackage, getMetadataAndModules, ZRO_MODULES } from "../utils" 6 | import { Environment } from "../../src/types" 7 | 8 | export async function deployZro(env: Environment, account: aptos.AptosAccount) { 9 | const address = account.address().toString() 10 | console.log({ 11 | env, 12 | address, 13 | }) 14 | 15 | if (env === Environment.LOCAL) { 16 | const faucet = new aptos.FaucetClient(NODE_URL[env], FAUCET_URL[env]) 17 | await faucet.fundAccount(address, 1000000000) 18 | } 19 | 20 | const sdk = new layerzero.SDK({ 21 | provider: new aptos.AptosClient(NODE_URL[env]), 22 | }) 23 | 24 | // compile and deploy zro 25 | const packagePath = path.join(__dirname, "../../../zro") 26 | await compilePackage(packagePath, packagePath, { zro: address }) 27 | const { metadata, modules } = getMetadataAndModules(packagePath, ZRO_MODULES) 28 | await sdk.deploy(account, metadata, modules) 29 | 30 | console.log("Deployed ZRO!!") 31 | } 32 | -------------------------------------------------------------------------------- /msglib/msglib-v2/sources/msglib_v2_router.move: -------------------------------------------------------------------------------- 1 | module msglib_v2::msglib_v2_router { 2 | use layerzero_common::packet::Packet; 3 | use aptos_framework::aptos_coin::AptosCoin; 4 | use aptos_framework::coin::Coin; 5 | use zro::zro::ZRO; 6 | use msglib_auth::msglib_cap::MsgLibSendCapability; 7 | 8 | const ELAYERZERO_NOT_SUPPORTED: u64 = 0x00; 9 | 10 | public fun send( 11 | _packet: &Packet, 12 | _native_fee: Coin, 13 | _zro_fee: Coin, 14 | _msglib_params: vector, 15 | _cap: &MsgLibSendCapability 16 | ): (Coin, Coin) { 17 | abort ELAYERZERO_NOT_SUPPORTED 18 | } 19 | 20 | public fun quote(_ua_address: address, _dst_chain_id: u64, _payload_size: u64, _pay_in_zro: bool, _msglib_params: vector): (u64, u64) { 21 | abort ELAYERZERO_NOT_SUPPORTED 22 | } 23 | 24 | public fun set_ua_config(_chain_id: u64, _config_type: u8, _config_bytes: vector, _cap: &MsgLibSendCapability) { 25 | abort ELAYERZERO_NOT_SUPPORTED 26 | } 27 | 28 | public fun get_ua_config(_ua_address: address, _chain_id: u64, _config_type: u8): vector{ 29 | abort ELAYERZERO_NOT_SUPPORTED 30 | } 31 | } -------------------------------------------------------------------------------- /msglib/msglib-v1/msglib-v1-1/sources/msglib_v1_1.move: -------------------------------------------------------------------------------- 1 | module msglib_v1_1::msglib_v1_1 { 2 | use layerzero_common::packet::Packet; 3 | use aptos_framework::aptos_coin::AptosCoin; 4 | use aptos_framework::coin::Coin; 5 | use zro::zro::ZRO; 6 | use msglib_auth::msglib_cap::MsgLibSendCapability; 7 | 8 | const ELAYERZERO_NOT_SUPPORTED: u64 = 0x00; 9 | 10 | public fun send( 11 | _packet: &Packet, 12 | _native_fee: Coin, 13 | _zro_fee: Coin, 14 | _msglib_params: vector, 15 | _cap: &MsgLibSendCapability 16 | ): (Coin, Coin) { 17 | abort ELAYERZERO_NOT_SUPPORTED 18 | } 19 | 20 | public fun quote(_ua_address: address, _dst_chain_id: u64, _payload_size: u64, _pay_in_zro: bool, _msglib_params: vector): (u64, u64) { 21 | abort ELAYERZERO_NOT_SUPPORTED 22 | } 23 | 24 | public fun set_ua_config(_chain_id: u64, _config_type: u8, _config_bytes: vector, _cap: &MsgLibSendCapability) { 25 | abort ELAYERZERO_NOT_SUPPORTED 26 | } 27 | 28 | public fun get_ua_config(_ua_address: address, _chain_id: u64, _config_type: u8): vector{ 29 | abort ELAYERZERO_NOT_SUPPORTED 30 | } 31 | } -------------------------------------------------------------------------------- /msglib/msglib-v1/msglib-v1-1/sources/msglib_v1_1_router.move: -------------------------------------------------------------------------------- 1 | module msglib_v1_1::msglib_v1_1_router { 2 | use layerzero_common::packet::Packet; 3 | use aptos_framework::aptos_coin::AptosCoin; 4 | use aptos_framework::coin::Coin; 5 | use zro::zro::ZRO; 6 | use msglib_auth::msglib_cap::MsgLibSendCapability; 7 | 8 | const ELAYERZERO_NOT_SUPPORTED: u64 = 0x00; 9 | 10 | public fun send( 11 | _packet: &Packet, 12 | _native_fee: Coin, 13 | _zro_fee: Coin, 14 | _msglib_params: vector, 15 | _cap: &MsgLibSendCapability 16 | ): (Coin, Coin) { 17 | abort ELAYERZERO_NOT_SUPPORTED 18 | } 19 | 20 | public fun quote(_ua_address: address, _dst_chain_id: u64, _payload_size: u64, _pay_in_zro: bool, _msglib_params: vector): (u64, u64) { 21 | abort ELAYERZERO_NOT_SUPPORTED 22 | } 23 | 24 | public fun set_ua_config(_chain_id: u64, _config_type: u8, _config_bytes: vector, _cap: &MsgLibSendCapability) { 25 | abort ELAYERZERO_NOT_SUPPORTED 26 | } 27 | 28 | public fun get_ua_config(_ua_address: address, _chain_id: u64, _config_type: u8): vector{ 29 | abort ELAYERZERO_NOT_SUPPORTED 30 | } 31 | } -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/config/addresses/testnetAddresses.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@layerzerolabs/core-sdk" 2 | 3 | type TestnetChainId = 4 | | ChainId.GOERLI 5 | | ChainId.BSC_TESTNET 6 | | ChainId.FUJI 7 | | ChainId.MUMBAI 8 | | ChainId.FANTOM_TESTNET 9 | | ChainId.ARBITRUM_GOERLI 10 | | ChainId.OPTIMISM_GOERLI 11 | 12 | export const WETH_ADDRESS: { [chainId in TestnetChainId]?: string } = { 13 | [ChainId.GOERLI]: "0xcC0235a403E77C56d0F271054Ad8bD3ABcd21904", 14 | } 15 | 16 | export const USDT_ADDRESS: { [chainId in TestnetChainId]?: string } = { 17 | [ChainId.BSC_TESTNET]: "0xF49E250aEB5abDf660d643583AdFd0be41464EfD", 18 | [ChainId.FUJI]: "0x134Dc38AE8C853D1aa2103d5047591acDAA16682", 19 | [ChainId.MUMBAI]: "0x6Fc340be8e378c2fF56476409eF48dA9a3B781a0", 20 | [ChainId.FANTOM_TESTNET]: "0x4a4129978218e7bac738D7B841D3F382D6EFbeE9", 21 | } 22 | 23 | export const USDC_ADDRESS: { [chainId in TestnetChainId]?: string } = { 24 | [ChainId.GOERLI]: "0x30c212b53714daf3739Ff607AaA8A0A18956f13c", 25 | [ChainId.FUJI]: "0x4A0D1092E9df255cf95D72834Ea9255132782318", 26 | [ChainId.MUMBAI]: "0x742DfA5Aa70a8212857966D491D67B09Ce7D6ec7", 27 | [ChainId.FANTOM_TESTNET]: "0x076488D244A73DA4Fa843f5A8Cd91F655CA81a1e", 28 | } 29 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/config/mainnet.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@layerzerolabs/core-sdk" 2 | import { CoinType } from "../../../../sdk/src/modules/apps/coin" 3 | import { PacketType } from "./types" 4 | import { getConfig } from "../../../../sdk/tasks/config/mainnet" 5 | 6 | export const CONFIG = { 7 | useCustomAdapterParams: true, 8 | feeBP: 0, 9 | minDstGas: { 10 | [PacketType.SEND_TO_APTOS]: 2500, 11 | }, 12 | coins: { 13 | [ChainId.ETHEREUM]: getCoinsFromAptosConfig(ChainId.ETHEREUM), 14 | [ChainId.AVALANCHE]: getCoinsFromAptosConfig(ChainId.AVALANCHE), 15 | [ChainId.POLYGON]: getCoinsFromAptosConfig(ChainId.POLYGON), 16 | [ChainId.BSC]: getCoinsFromAptosConfig(ChainId.BSC), 17 | [ChainId.ARBITRUM]: getCoinsFromAptosConfig(ChainId.ARBITRUM), 18 | [ChainId.OPTIMISM]: getCoinsFromAptosConfig(ChainId.OPTIMISM), 19 | }, 20 | } 21 | 22 | function getCoinsFromAptosConfig(chainId: ChainId): CoinType[] { 23 | const types = [] 24 | const coins = getConfig([]).bridge.coins 25 | for (const coin in coins) { 26 | const remotes = Object.keys(coins[coin].remotes) 27 | if (remotes.includes(chainId.toString())) { 28 | types.push(coin as CoinType) 29 | } 30 | } 31 | return types 32 | } 33 | -------------------------------------------------------------------------------- /executor/executor-auth/sources/executor_cap.move: -------------------------------------------------------------------------------- 1 | module executor_auth::executor_cap { 2 | use std::error; 3 | use layerzero_common::utils::assert_signer; 4 | 5 | const EEXECUTOR_AUTH_NOT_AUTORIZED: u64 = 0x00; 6 | 7 | struct GlobalStore has key { 8 | last_version: u64 9 | } 10 | 11 | struct ExecutorCapability has store { 12 | version: u64 13 | } 14 | 15 | fun init_module(account: &signer) { 16 | move_to(account, GlobalStore { last_version: 0 }) 17 | } 18 | 19 | public fun new_version(account: &signer): (u64, ExecutorCapability) acquires GlobalStore { 20 | assert_signer(account, @executor_auth); 21 | let store = borrow_global_mut(@executor_auth); 22 | store.last_version = store.last_version + 1; 23 | (store.last_version, ExecutorCapability { version: store.last_version }) 24 | } 25 | 26 | public fun version(cap: &ExecutorCapability): u64 { 27 | cap.version 28 | } 29 | 30 | public fun assert_version(cap: &ExecutorCapability, version: u64) { 31 | assert!(cap.version == version, error::invalid_argument(EEXECUTOR_AUTH_NOT_AUTORIZED)); 32 | } 33 | 34 | #[test_only] 35 | public fun init_module_for_test(account: &signer) { 36 | init_module(account); 37 | } 38 | } -------------------------------------------------------------------------------- /sdk/tasks/deploy/executor/deployExecutorV2.ts: -------------------------------------------------------------------------------- 1 | import { Environment } from "../../../src/types" 2 | import * as aptos from "aptos" 3 | import { FAUCET_URL, NODE_URL } from "../../../src/constants" 4 | import * as layerzero from "../../../src" 5 | import * as path from "path" 6 | import { compilePackage, EXECUTOR_V2_MODULES, getMetadataAndModules } from "../../utils" 7 | 8 | export async function deployExecutorV2(env: Environment, account: aptos.AptosAccount) { 9 | const address = account.address().toString() 10 | console.log({ 11 | env, 12 | address, 13 | }) 14 | if (env === Environment.LOCAL) { 15 | const faucet = new aptos.FaucetClient(NODE_URL[env], FAUCET_URL[env]) 16 | await faucet.fundAccount(address, 1000000000) 17 | } 18 | 19 | const sdk = new layerzero.SDK({ 20 | provider: new aptos.AptosClient(NODE_URL[env]), 21 | }) 22 | 23 | // compile and deploy executor v2 24 | const packagePath = path.join(__dirname, "../../../../executor/executor-v2") 25 | await compilePackage(packagePath, packagePath, { 26 | layerzero_common: address, 27 | executor_auth: address, 28 | executor_v2: address, 29 | }) 30 | const { metadata, modules } = getMetadataAndModules(packagePath, EXECUTOR_V2_MODULES) 31 | await sdk.deploy(account, metadata, modules) 32 | 33 | console.log("Deployed executor v2!!") 34 | } 35 | -------------------------------------------------------------------------------- /sdk/tasks/deploy/msglib/deployMsglibV2.ts: -------------------------------------------------------------------------------- 1 | import { Environment } from "../../../src/types" 2 | import * as aptos from "aptos" 3 | import { FAUCET_URL, NODE_URL } from "../../../src/constants" 4 | import * as layerzero from "../../../src" 5 | import * as path from "path" 6 | import { compilePackage, getMetadataAndModules, MSGLIB_V2_MODUELS } from "../../utils" 7 | 8 | export async function deployMsglibV2(env: Environment, account: aptos.AptosAccount) { 9 | const address = account.address().toString() 10 | console.log({ 11 | env, 12 | address, 13 | }) 14 | if (env === Environment.LOCAL) { 15 | const faucet = new aptos.FaucetClient(NODE_URL[env], FAUCET_URL[env]) 16 | await faucet.fundAccount(address, 1000000000) 17 | } 18 | 19 | const sdk = new layerzero.SDK({ 20 | provider: new aptos.AptosClient(NODE_URL[env]), 21 | }) 22 | 23 | // compile and deploy msglib v1.1 24 | const packagePath = path.join(__dirname, "../../../../msglib/msglib-v2") 25 | await compilePackage(packagePath, packagePath, { 26 | layerzero_common: address, 27 | msglib_auth: address, 28 | msglib_v2: address, 29 | zro: address, 30 | }) 31 | const { metadata, modules } = getMetadataAndModules(packagePath, MSGLIB_V2_MODUELS) 32 | await sdk.deploy(account, metadata, modules) 33 | 34 | console.log("Deployed msg lib v2!!") 35 | } 36 | -------------------------------------------------------------------------------- /sdk/tasks/deploy/msglib/deployMsglibV1_1.ts: -------------------------------------------------------------------------------- 1 | import { Environment } from "../../../src/types" 2 | import * as aptos from "aptos" 3 | import { FAUCET_URL, NODE_URL } from "../../../src/constants" 4 | import * as layerzero from "../../../src" 5 | import * as path from "path" 6 | import { compilePackage, getMetadataAndModules, MSGLIB_V1_1_MODUELS } from "../../utils" 7 | 8 | export async function deployMsglibV1_1(env: Environment, account: aptos.AptosAccount) { 9 | const address = account.address().toString() 10 | console.log({ 11 | env, 12 | address, 13 | }) 14 | if (env === Environment.LOCAL) { 15 | const faucet = new aptos.FaucetClient(NODE_URL[env], FAUCET_URL[env]) 16 | await faucet.fundAccount(address, 1000000000) 17 | } 18 | 19 | const sdk = new layerzero.SDK({ 20 | provider: new aptos.AptosClient(NODE_URL[env]), 21 | }) 22 | 23 | // compile and deploy msglib v1.1 24 | const packagePath = path.join(__dirname, "../../../../msglib/msglib-v1/msglib-v1-1") 25 | await compilePackage(packagePath, packagePath, { 26 | layerzero_common: address, 27 | msglib_auth: address, 28 | msglib_v1_1: address, 29 | zro: address, 30 | }) 31 | const { metadata, modules } = getMetadataAndModules(packagePath, MSGLIB_V1_1_MODUELS) 32 | await sdk.deploy(account, metadata, modules) 33 | 34 | console.log("Deployed msg lib v1.1!!") 35 | } 36 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/index.ts: -------------------------------------------------------------------------------- 1 | import { subtask, task, types } from "hardhat/config" 2 | 3 | subtask("deployBridge", "", require("./deployBridge")) 4 | .addParam("networks", "comma separated list of networks to deploy the bridge on") 5 | .addOptionalParam("deleteOldDeploy", "whether to delete old deployments", false, types.boolean) 6 | 7 | task("testBridge", "", require("./testBridge")) 8 | 9 | task("send", "", require("./send")) 10 | .addParam("a", "amount", "1000000000") 11 | .addParam("t", "token type", "ETH") 12 | .addParam("r", "address of receiver at aptos", "0x5d96ae95d5ba826af7a1799d824a9581a4bb75c194556a11bb85ef0f5b6e973a") 13 | 14 | task("mint", "", require("./mint")).addParam("t", "token type", "ETH").addParam("a", "address", "0x15e36adEBB0c65217Eab8cdd58BD69cf1FAa24c2") 15 | 16 | task("wireAll", "", require("./wireAll")) 17 | .addParam("e", "the environment ie: mainnet, testnet or sandbox", "sandbox") 18 | .addOptionalParam("srcNetworks", "comma seperated list of networks to config on", "goerli-sandbox", types.string) 19 | .addParam("noPrompt", "no prompt", false, types.boolean) 20 | 21 | subtask("wireAllSubtask", "", require("./wireAll")) 22 | .addParam("e", "the environment ie: mainnet, testnet or sandbox", "sandbox") 23 | .addParam("srcNetworks", "comma seperated list of networks to config on", "goerli-sandbox", types.string) 24 | .addParam("noPrompt", "no prompt", false, types.boolean) 25 | -------------------------------------------------------------------------------- /apps/bridge-evm/test/foundry/TokenBridgeProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | 3 | pragma solidity 0.8.15; 4 | 5 | import "../../contracts/TokenBridge.sol"; 6 | import "../../contracts/mocks/LZEndpointMock.sol"; 7 | 8 | // the proxy to make internal functions public for testing 9 | contract TokenBridgeProxy is TokenBridge { 10 | constructor( 11 | address _layerZeroEndpoint, 12 | uint16 _localChainId, 13 | uint16 _aptosChainId 14 | ) TokenBridge(_layerZeroEndpoint, _localChainId, _aptosChainId) {} 15 | 16 | function encodeSendPayload( 17 | address _token, 18 | bytes32 _toAddress, 19 | uint64 _amountSD 20 | ) external pure returns (bytes memory) { 21 | return _encodeSendPayload(_token, _toAddress, _amountSD); 22 | } 23 | 24 | function decodeReceivePayload(bytes calldata _payload) 25 | external 26 | pure 27 | returns ( 28 | address token, 29 | address to, 30 | uint64 amountSD, 31 | bool unwrap 32 | ) 33 | { 34 | return _decodeReceivePayload(_payload); 35 | } 36 | 37 | function SDtoLD(address _token, uint64 _amountSD) external view returns (uint) { 38 | return _SDtoLD(_token, _amountSD); 39 | } 40 | 41 | function LDtoSD(address _token, uint _amountLD) external view returns (uint64) { 42 | return _LDtoSD(_token, _amountLD); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /layerzero/sources/executor_router.move: -------------------------------------------------------------------------------- 1 | module layerzero::executor_router { 2 | use aptos_framework::aptos_coin::AptosCoin; 3 | use aptos_framework::coin::Coin; 4 | use layerzero::executor_v1; 5 | use layerzero_common::packet::Packet; 6 | use executor_v2::executor_v2; 7 | use executor_auth::executor_cap::{Self, ExecutorCapability}; 8 | 9 | friend layerzero::endpoint; 10 | 11 | // interacting with the currently configured version 12 | public(friend) fun request( 13 | executor: address, 14 | packet: &Packet, 15 | adapter_params: vector, 16 | fee: Coin, 17 | cap: &ExecutorCapability, 18 | ): Coin { 19 | let version = executor_cap::version(cap); 20 | if (version == 1) { 21 | executor_v1::request(executor, packet, adapter_params, fee, cap) 22 | } else { 23 | executor_v2::request(executor, packet, adapter_params, fee, cap) 24 | } 25 | } 26 | 27 | public fun quote( 28 | ua_address: address, 29 | version: u64, 30 | executor: address, 31 | dst_chain_id: u64, 32 | adapter_params: vector 33 | ): u64 { 34 | if (version == 1) { 35 | executor_v1::quote_fee(ua_address, executor, dst_chain_id, adapter_params) 36 | } else { 37 | executor_v2::quote_fee(ua_address, executor, dst_chain_id, adapter_params) 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /executor/executor-ext/sources/executor_ext.move: -------------------------------------------------------------------------------- 1 | module executor_ext::executor_ext { 2 | use aptos_std::type_info; 3 | use std::signer; 4 | 5 | struct TypeArguments has key { 6 | types: vector 7 | } 8 | 9 | public fun build_lz_receive_types(account: &signer, types: vector) acquires TypeArguments { 10 | if (exists(signer::address_of(account))) { 11 | let arguments = move_from(signer::address_of(account)); 12 | let TypeArguments { types: _ } = arguments; 13 | }; 14 | 15 | move_to(account, TypeArguments { types }); 16 | } 17 | 18 | #[test_only] 19 | struct ExampleType {} 20 | 21 | #[test_only] 22 | struct ExampleType2 {} 23 | 24 | #[test(account = @0xDEAD)] 25 | fun test_lz_receive_types(account: &signer) acquires TypeArguments{ 26 | let type_args = vector[type_info::type_of(), type_info::type_of()]; 27 | build_lz_receive_types(account, type_args); 28 | let args = borrow_global(signer::address_of(account)); 29 | assert!(type_args == args.types, 2); 30 | 31 | let type_args = vector[type_info::type_of()]; 32 | build_lz_receive_types(account, type_args); 33 | let args = borrow_global(signer::address_of(account)); 34 | assert!(type_args == args.types, 3) 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/utils/readStatic.ts: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | const fs = require("fs") 3 | 4 | export function getDeploymentAddresses(networkName: string) { 5 | const PROJECT_ROOT = path.resolve(__dirname, "../..") 6 | const DEPLOYMENT_PATH = path.resolve(PROJECT_ROOT, "deployments") 7 | 8 | let folderName = networkName 9 | if (networkName === "hardhat") { 10 | folderName = "localhost" 11 | } 12 | 13 | const networkFolderName = fs.readdirSync(DEPLOYMENT_PATH).filter((f) => f === folderName)[0] 14 | if (networkFolderName === undefined) { 15 | throw new Error("missing deployment files for endpoint " + folderName) 16 | } 17 | 18 | let rtnAddresses: { [name: string]: string } = {} 19 | const networkFolderPath = path.resolve(DEPLOYMENT_PATH, folderName) 20 | const files = fs.readdirSync(networkFolderPath).filter((f) => f.includes(".json")) 21 | files.forEach((file) => { 22 | const filepath = path.resolve(networkFolderPath, file) 23 | const data = JSON.parse(fs.readFileSync(filepath, "utf8")) 24 | const contractName = file.split(".")[0] 25 | rtnAddresses[contractName] = data.address 26 | }) 27 | 28 | return rtnAddresses 29 | } 30 | 31 | export function getRpc(network) { 32 | try { 33 | return require("../../hardhat.config").default.networks[network].url 34 | } catch (e) { 35 | throw `getRpc failed to get RPC URL for >> ${network} << -- do you REALLY have this network configured properly in hardhat.config.ts??` 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sdk/src/modules/msglib_auth.ts: -------------------------------------------------------------------------------- 1 | import { SDK } from "../index" 2 | import * as aptos from "aptos" 3 | 4 | export class MsgLibAuth { 5 | public readonly module 6 | public readonly moduleName 7 | 8 | constructor(private sdk: SDK) { 9 | this.module = `${sdk.accounts.msglib_auth}::msglib_cap` 10 | this.moduleName = "msglib_auth::msglib_cap" 11 | } 12 | 13 | async isAllowed(msglibReceive: string): Promise { 14 | const resource: { data: any } = await this.sdk.client.getAccountResource( 15 | this.sdk.accounts.msglib_auth!, 16 | `${this.module}::GlobalStore`, 17 | ) 18 | const msglibAcl = resource.data.msglib_acl.list 19 | const lib = aptos.HexString.ensure(msglibReceive).toShortString() 20 | return msglibAcl.includes(lib) 21 | } 22 | 23 | denyPayload(msglibReceive: string): aptos.Types.EntryFunctionPayload { 24 | return { 25 | function: `${this.module}::deny`, 26 | type_arguments: [], 27 | arguments: [msglibReceive], 28 | } 29 | } 30 | 31 | allowPayload(msglibReceive: string): aptos.Types.EntryFunctionPayload { 32 | return { 33 | function: `${this.module}::allow`, 34 | type_arguments: [], 35 | arguments: [msglibReceive], 36 | } 37 | } 38 | 39 | async allow(signer: aptos.AptosAccount, msglibReceive: string): Promise { 40 | const transaction = this.allowPayload(msglibReceive) 41 | return this.sdk.sendAndConfirmTransaction(signer, transaction) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sdk/tasks/options.ts: -------------------------------------------------------------------------------- 1 | import * as commander from "commander" 2 | import { Environment } from "../src/types" 3 | import { ChainStage } from "@layerzerolabs/core-sdk" 4 | 5 | export const OPTION_PROMPT = new commander.Option("-p, --prompt ", "prompt for confirmation").default(true) 6 | 7 | export const OPTION_ENV = new commander.Option("-e, --env ", "aptos chain environment") 8 | .default(Environment.LOCAL) 9 | .choices([Environment.LOCAL, Environment.DEVNET, Environment.TESTNET, Environment.MAINNET]) 10 | 11 | export const OPTION_STAGE = new commander.Option("-s, --stage ", "stage for lookup and configurations") 12 | .makeOptionMandatory(true) 13 | .choices(["sandbox", "testnet", "mainnet"]) 14 | .argParser(function getChainStage(stage: string) { 15 | switch (stage) { 16 | case "sandbox": 17 | return ChainStage.TESTNET_SANDBOX 18 | case "testnet": 19 | return ChainStage.TESTNET 20 | case "mainnet": 21 | return ChainStage.MAINNET 22 | default: 23 | throw new Error(`Invalid stage: ${stage}`) 24 | } 25 | }) 26 | 27 | export const OPTION_TO_NETWORKS = new commander.Option("-t, --to-networks ", "to networks") 28 | .makeOptionMandatory(true) 29 | .argParser(function commaSeparatedList(value: string, prev: string[]): string[] { 30 | return value.split(",") 31 | }) 32 | 33 | export const OPTION_KEY_PATH = new commander.Option("-k, --key-path ", "keypair base path").default( 34 | "~/.config/aptos", 35 | ) 36 | 37 | export const OPTION_FORKED = new commander.Option("-f, --forked ", "forked").default(false) 38 | -------------------------------------------------------------------------------- /layerzero/sources/msglib-v1/v1-0/packet_event.move: -------------------------------------------------------------------------------- 1 | module layerzero::packet_event { 2 | use aptos_std::event::EventHandle; 3 | use aptos_framework::account; 4 | use layerzero_common::packet::{Packet, encode_packet}; 5 | use aptos_std::event; 6 | 7 | friend layerzero::msglib_v1_0; 8 | friend layerzero::uln_receive; 9 | 10 | struct InboundEvent has drop, store { 11 | packet: Packet, 12 | } 13 | 14 | struct OutboundEvent has drop, store { 15 | encoded_packet: vector, 16 | } 17 | 18 | struct EventStore has key { 19 | inbound_events: EventHandle, 20 | outbound_events: EventHandle, 21 | } 22 | 23 | fun init_module(account: &signer) { 24 | move_to(account, EventStore { 25 | inbound_events: account::new_event_handle(account), 26 | outbound_events: account::new_event_handle(account), 27 | }); 28 | } 29 | 30 | public(friend) fun emit_inbound_event(packet: Packet) acquires EventStore { 31 | let event_store = borrow_global_mut(@layerzero); 32 | event::emit_event( 33 | &mut event_store.inbound_events, 34 | InboundEvent { packet }, 35 | ); 36 | } 37 | 38 | public(friend) fun emit_outbound_event(packet: &Packet) acquires EventStore { 39 | let event_store = borrow_global_mut(@layerzero); 40 | event::emit_event( 41 | &mut event_store.outbound_events, 42 | OutboundEvent { encoded_packet: encode_packet(packet) }, 43 | ); 44 | } 45 | 46 | #[test_only] 47 | public fun init_module_for_test(account: &signer) { 48 | init_module(account); 49 | } 50 | } -------------------------------------------------------------------------------- /apps/bridge-evm/deploy/TokenBridge.ts: -------------------------------------------------------------------------------- 1 | import { ChainId, CHAIN_STAGE, LZ_ADDRESS, ChainStage } from "@layerzerolabs/core-sdk" 2 | import * as crossChainHelper from "../tasks/utils/crossChainHelper" 3 | 4 | module.exports = async function ({ ethers, deployments, getNamedAccounts, network }) { 5 | const { deploy } = deployments 6 | const { deployer } = await getNamedAccounts() 7 | console.log(deployer) 8 | if (crossChainHelper.FORKING) { 9 | const provider = await crossChainHelper.getProvider(network.name) 10 | await provider.send("hardhat_impersonateAccount", [deployer]) 11 | await provider.send("hardhat_setBalance", [deployer, "0x1000000000000000000000000000000000000000000000000000000000000000"]) 12 | } 13 | 14 | console.log(`Network: ${network.name}`) 15 | 16 | const name = network.name.replace("-fork", "") 17 | 18 | const address = LZ_ADDRESS[name] ?? ethers.constants.AddressZero 19 | console.log(`Network: ${name}, Endpoint Address: ${address}`) 20 | 21 | let aptosId 22 | switch (CHAIN_STAGE[name]) { 23 | case ChainStage.MAINNET: 24 | aptosId = ChainId.APTOS 25 | break 26 | case ChainStage.TESTNET: 27 | aptosId = ChainId.APTOS_TESTNET 28 | break 29 | case ChainStage.TESTNET_SANDBOX: 30 | aptosId = ChainId.APTOS_TESTNET_SANDBOX 31 | break 32 | default: 33 | throw new Error("Invalid chain stage") 34 | } 35 | console.log(`Aptos Id: ${aptosId}`) 36 | 37 | await deploy("TokenBridge", { 38 | from: deployer, 39 | args: [address, aptosId], 40 | log: true, 41 | waitConfirmations: 1, 42 | skipIfAlreadyDeployed: true, 43 | }) 44 | } 45 | 46 | module.exports.tags = ["TokenBridge"] 47 | -------------------------------------------------------------------------------- /sdk/src/modules/uln/packet_event.ts: -------------------------------------------------------------------------------- 1 | import { SDK } from "../../index" 2 | import * as aptos from "aptos" 3 | import BN from "bn.js" 4 | 5 | export class PacketEvent { 6 | public readonly module 7 | 8 | constructor(private sdk: SDK) { 9 | this.module = `${sdk.accounts.layerzero}::packet_event` 10 | } 11 | 12 | async getInboundEvents(start: bigint, limit: number): Promise { 13 | return this.sdk.client.getEventsByEventHandle( 14 | this.sdk.accounts.layerzero!, 15 | `${this.module}::EventStore`, 16 | "inbound_events", 17 | { start, limit }, 18 | ) 19 | } 20 | 21 | async getInboundEventCount(): Promise { 22 | const resource = await this.sdk.client.getAccountResource( 23 | this.sdk.accounts.layerzero!, 24 | `${this.module}::EventStore`, 25 | ) 26 | const { inbound_events } = resource.data as { inbound_events: { counter: string } } 27 | return new BN(inbound_events.counter).toNumber() 28 | } 29 | 30 | async getOutboundEvents(start: bigint, limit: number): Promise { 31 | return this.sdk.client.getEventsByEventHandle( 32 | this.sdk.accounts.layerzero!, 33 | `${this.module}::EventStore`, 34 | "outbound_events", 35 | { start, limit }, 36 | ) 37 | } 38 | 39 | async getOutboundEventCount(): Promise { 40 | const resource = await this.sdk.client.getAccountResource( 41 | this.sdk.accounts.layerzero!, 42 | `${this.module}::EventStore`, 43 | ) 44 | const { outbound_events } = resource.data as { outbound_events: { counter: string } } 45 | return new BN(outbound_events.counter).toNumber() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /layerzero-common/sources/semver.move: -------------------------------------------------------------------------------- 1 | module layerzero_common::semver { 2 | use layerzero_common::utils::assert_u16; 3 | 4 | // reserved msg lib versions 5 | const DEFAULT_VERSION: u64 = 0; 6 | const BLOCK_VERSION: u64 = 65535; 7 | 8 | // a semantic version representation 9 | // major version starts from 1 and minor version starts from 0 10 | struct SemVer has drop, store, copy { 11 | major: u64, 12 | minor: u8, 13 | } 14 | 15 | public fun next_major(v: &SemVer): SemVer { 16 | build_version(v.major + 1, 0) 17 | } 18 | 19 | public fun next_minor(v: &SemVer): SemVer { 20 | build_version(v.major, v.minor + 1) 21 | } 22 | 23 | public fun major(v: &SemVer): u64 { 24 | v.major 25 | } 26 | 27 | public fun minor(v: &SemVer): u8 { 28 | v.minor 29 | } 30 | 31 | public fun values(v: &SemVer): (u64, u8) { 32 | (v.major, v.minor) 33 | } 34 | 35 | public fun build_version(major: u64, minor: u8): SemVer { 36 | assert_u16(major); 37 | SemVer { 38 | major, 39 | minor, 40 | } 41 | } 42 | 43 | public fun default_version(): SemVer { 44 | SemVer { 45 | major: DEFAULT_VERSION, 46 | minor: 0, 47 | } 48 | } 49 | 50 | public fun blocking_version(): SemVer { 51 | SemVer { 52 | major: BLOCK_VERSION, 53 | minor: 0, 54 | } 55 | } 56 | 57 | public fun is_blocking(v: &SemVer): bool { 58 | v.major == BLOCK_VERSION 59 | } 60 | 61 | public fun is_default(v: &SemVer): bool { 62 | v.major == DEFAULT_VERSION 63 | } 64 | 65 | public fun is_blocking_or_default(v: &SemVer): bool { 66 | v.major == BLOCK_VERSION || v.major == DEFAULT_VERSION 67 | } 68 | } -------------------------------------------------------------------------------- /apps/bridge-evm/contracts/interfaces/ITokenBridge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | 3 | pragma solidity >=0.6.0; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "@layerzerolabs/solidity-examples/contracts/libraries/LzLib.sol"; 7 | 8 | interface ITokenBridge { 9 | enum PacketType { 10 | SEND_TO_APTOS, 11 | RECEIVE_FROM_APTOS 12 | } 13 | 14 | function sendToAptos( 15 | address _token, 16 | bytes32 _toAddress, 17 | uint _amountLD, 18 | LzLib.CallParams calldata _callParams, 19 | bytes calldata _adapterParams 20 | ) external payable; 21 | 22 | function sendETHToAptos( 23 | bytes32 _toAddress, 24 | uint _amountLD, 25 | LzLib.CallParams calldata _callParams, 26 | bytes calldata _adapterParams 27 | ) external payable; 28 | 29 | function quoteForSend(LzLib.CallParams calldata _callParams, bytes calldata _adapterParams) 30 | external 31 | view 32 | returns (uint nativeFee, uint zroFee); 33 | 34 | event Send(address indexed token, address indexed from, bytes32 indexed to, uint amountLD); 35 | event Receive(address indexed token, address indexed to, uint amountLD); 36 | event RegisterToken(address token); 37 | event SetBridgeBP(uint bridgeFeeBP); 38 | event SetWETH(address weth); 39 | event SetGlobalPause(bool paused); 40 | event SetTokenPause(address token, bool paused); 41 | event SetLocalChainId(uint16 localChainId); 42 | event SetAptosChainId(uint16 aptosChainId); 43 | event SetUseCustomAdapterParams(bool useCustomAdapterParams); 44 | event WithdrawFee(address indexed token, address to, uint amountLD); 45 | event WithdrawTVL(address indexed token, address to, uint amountLD); 46 | event EnableEmergencyWithdraw(bool enabled, uint unlockTime); 47 | } 48 | -------------------------------------------------------------------------------- /sdk/tasks/config/common.ts: -------------------------------------------------------------------------------- 1 | import { ChainId, ChainStage } from "@layerzerolabs/core-sdk" 2 | 3 | export type ConfigType = { 4 | msglib: { 5 | msglibv1: any 6 | } 7 | endpoint: any 8 | executor: any 9 | relayer: any 10 | oracle: any 11 | bridge: any 12 | } 13 | 14 | export const NETWORK_NAME: { [c in ChainStage]?: string } = { 15 | [ChainStage.MAINNET]: "aptos", 16 | [ChainStage.TESTNET]: "aptos-testnet", 17 | [ChainStage.TESTNET_SANDBOX]: "aptos-testnet-sandbox", 18 | } 19 | 20 | export const EVM_ADDERSS_SIZE = 20 21 | 22 | export const ARBITRUM_MULTIPLIER = 10 23 | 24 | export function applyArbitrumMultiplier(chainId: ChainId, value: number) { 25 | return [ChainId.ARBITRUM_GOERLI, ChainId.ARBITRUM_GOERLI_SANDBOX, ChainId.ARBITRUM].includes(chainId) 26 | ? value * ARBITRUM_MULTIPLIER 27 | : value 28 | } 29 | 30 | export const DEFAULT_BLOCK_CONFIRMATIONS: { [chainStage in ChainStage]?: { [chainId in ChainId]?: number } } = { 31 | [ChainStage.MAINNET]: { 32 | [ChainId.ETHEREUM]: 15, 33 | [ChainId.BSC]: 20, 34 | [ChainId.AVALANCHE]: 12, 35 | [ChainId.POLYGON]: 512, 36 | [ChainId.ARBITRUM]: 20, 37 | [ChainId.OPTIMISM]: 20, 38 | [ChainId.FANTOM]: 5, 39 | }, 40 | [ChainStage.TESTNET]: { 41 | [ChainId.GOERLI]: 10, 42 | [ChainId.BSC_TESTNET]: 5, 43 | [ChainId.FUJI]: 6, 44 | [ChainId.MUMBAI]: 10, 45 | [ChainId.FANTOM_TESTNET]: 7, 46 | [ChainId.HARMONY_TESTNET]: 5, 47 | [ChainId.ARBITRUM_GOERLI]: 3, 48 | [ChainId.OPTIMISM_GOERLI]: 3, 49 | }, 50 | [ChainStage.TESTNET_SANDBOX]: { 51 | [ChainId.BSC_TESTNET_SANDBOX]: 5, 52 | [ChainId.FUJI_SANDBOX]: 6, 53 | [ChainId.MUMBAI_SANDBOX]: 10, 54 | [ChainId.FANTOM_TESTNET_SANDBOX]: 7, 55 | [ChainId.GOERLI_SANDBOX]: 10, 56 | }, 57 | } 58 | -------------------------------------------------------------------------------- /apps/bridge-evm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@layerzerolabs/bridge-evm-internal", 3 | "version": "0.0.1", 4 | "license": "BUSL-1.1", 5 | "scripts": { 6 | "build": "npx hardhat compile", 7 | "test": "npx hardhat test --config ./hardhat.config.ts", 8 | "dev": "npx hardhat node", 9 | "coverage": "npx hardhat coverage --config hardhat-coverage.config.ts --solcoverjs ./config/.solcover.js", 10 | "lint:check": "prettier --check **.sol && prettier --check **.js && prettier --check **.ts && hardhat compile --config hardhat.config.ts && npx solhint --config ./config/.solhint.json --ignore-path ./config/.solhintignore 'contracts/**/*.sol'", 11 | "lint:fix": "prettier --write **.sol && prettier --write **.js && prettier --write **.ts", 12 | "test:forge": "forge test -vvv" 13 | }, 14 | "resolutions": { 15 | "adm-zip": "0.5.2" 16 | }, 17 | "devDependencies": { 18 | "@layerzerolabs/d2-contracts": "^1.0.19", 19 | "@layerzerolabs/layerzero-core": "^0.0.42", 20 | "@layerzerolabs/lz-sdk": "^0.0.5", 21 | "@layerzerolabs/solidity-examples": "0.0.4", 22 | "@nomiclabs/hardhat-ethers": "^2.0.6", 23 | "@nomiclabs/hardhat-waffle": "^2.0.3", 24 | "@openzeppelin/contracts": "^4.6.0", 25 | "abi-decoder": "^2.4.0", 26 | "dotenv": "^16.0.1", 27 | "ethereum-waffle": "^3.4.4", 28 | "hardhat": "2.8.3", 29 | "hardhat-deploy": "^0.11.12", 30 | "hardhat-deploy-ethers": "^0.3.0-beta.13", 31 | "hardhat-gas-reporter": "^1.0.8", 32 | "hardhat-spdx-license-identifier": "^2.0.3", 33 | "prettier": "^2.7.1", 34 | "prettier-plugin-solidity": "^1.0.0-beta.19", 35 | "solhint": "^3.3.7", 36 | "solidity-coverage": "^0.7.21", 37 | "ts-node": "^10.8.1", 38 | "typescript": "^4.7.4" 39 | }, 40 | "dependencies": { 41 | "@layerzerolabs/core-sdk": "^1.0.41-beta.37", 42 | "cli-ux": "^6.0.9", 43 | "tiny-invariant": "^1.3.1" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/config/addresses/index.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@layerzerolabs/core-sdk" 2 | import { BridgeCoinType, CoinType } from "../../../../../sdk/src/modules/apps/coin" 3 | import { getDeploymentAddresses } from "../../utils/readStatic" 4 | import { 5 | WETH_ADDRESS as WETH_MAINNET_ADDRESS, 6 | USDT_ADDRESS as USDT_MAINNET_ADDRESS, 7 | USDC_ADDRESS as USDC_MAINNET_ADDRESS, 8 | } from "./mainnetAddresses" 9 | import { 10 | WETH_ADDRESS as WETH_TESTNET_ADDRESS, 11 | USDT_ADDRESS as USDT_TESTNET_ADDRESS, 12 | USDC_ADDRESS as USDC_TESTNET_ADDRESS, 13 | } from "./testnetAddresses" 14 | import { 15 | WETH_ADDRESS as WETH_SANDBOX_ADDRESS, 16 | USDT_ADDRESS as USDT_SANDBOX_ADDRESS, 17 | USDC_ADDRESS as USDC_SANDBOX_ADDRESS, 18 | } from "./sandboxAddresses" 19 | 20 | export const WETH_ADDRESS: { [chainId in ChainId]?: string } = { 21 | ...WETH_SANDBOX_ADDRESS, 22 | ...WETH_TESTNET_ADDRESS, 23 | ...WETH_MAINNET_ADDRESS, 24 | } 25 | 26 | export const USDC_ADDRESS: { [chainId in ChainId]?: string } = { 27 | ...USDC_SANDBOX_ADDRESS, 28 | ...USDC_TESTNET_ADDRESS, 29 | ...USDC_MAINNET_ADDRESS, 30 | } 31 | 32 | export const USDT_ADDRESS: { [chainId in ChainId]?: string } = { 33 | ...USDT_SANDBOX_ADDRESS, 34 | ...USDT_TESTNET_ADDRESS, 35 | ...USDT_MAINNET_ADDRESS, 36 | } 37 | 38 | export const TOKEN_ADDRESSES: { [type in BridgeCoinType]?: { [chainId in ChainId]?: string } } = { 39 | [CoinType.WETH]: WETH_ADDRESS, 40 | [CoinType.USDC]: USDC_ADDRESS, 41 | [CoinType.USDT]: USDT_ADDRESS, 42 | } 43 | 44 | const bridgeAddresses: { [key: string]: string } = {} 45 | export function evmBridgeAddresses(network: string, forked = false): string { 46 | if (forked) { 47 | network = `${network}-fork` 48 | } 49 | const key = `${network}}` 50 | if (!bridgeAddresses[key]) { 51 | bridgeAddresses[key] = getDeploymentAddresses(network)["TokenBridge"] 52 | } 53 | return bridgeAddresses[key] 54 | } 55 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/config/addresses/mainnetAddresses.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@layerzerolabs/core-sdk" 2 | 3 | type MainnetChainId = ChainId.ETHEREUM | ChainId.BSC | ChainId.AVALANCHE | ChainId.POLYGON | ChainId.ARBITRUM | ChainId.OPTIMISM | ChainId.FANTOM 4 | 5 | export const WETH_ADDRESS: { [chainId in MainnetChainId]?: string } = { 6 | [ChainId.ETHEREUM]: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", 7 | [ChainId.ARBITRUM]: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", 8 | [ChainId.OPTIMISM]: "0x4200000000000000000000000000000000000006", 9 | } 10 | 11 | export const USDT_ADDRESS: { [chainId in MainnetChainId]?: string } = { 12 | [ChainId.ETHEREUM]: "0xdAC17F958D2ee523a2206206994597C13D831ec7", 13 | [ChainId.BSC]: "0x55d398326f99059fF775485246999027B3197955", 14 | [ChainId.AVALANCHE]: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7", 15 | [ChainId.POLYGON]: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", 16 | } 17 | 18 | export const USDC_ADDRESS: { [chainId in MainnetChainId]?: string } = { 19 | [ChainId.ETHEREUM]: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", 20 | [ChainId.AVALANCHE]: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", 21 | [ChainId.POLYGON]: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", 22 | [ChainId.ARBITRUM]: "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", 23 | [ChainId.OPTIMISM]: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", 24 | } 25 | 26 | export const WETH_DECIMALS: { [chainId in MainnetChainId]?: number } = { 27 | [ChainId.ETHEREUM]: 18, 28 | [ChainId.ARBITRUM]: 18, 29 | [ChainId.OPTIMISM]: 18, 30 | } 31 | 32 | export const USDT_DECIMALS: { [chainId in MainnetChainId]?: number } = { 33 | [ChainId.ETHEREUM]: 6, 34 | [ChainId.BSC]: 18, 35 | [ChainId.AVALANCHE]: 6, 36 | [ChainId.POLYGON]: 6, 37 | } 38 | 39 | export const USDC_DECIMALS: { [chainId in MainnetChainId]?: number } = { 40 | [ChainId.ETHEREUM]: 6, 41 | [ChainId.AVALANCHE]: 6, 42 | [ChainId.POLYGON]: 6, 43 | [ChainId.ARBITRUM]: 6, 44 | [ChainId.OPTIMISM]: 6, 45 | } -------------------------------------------------------------------------------- /sdk/tasks/deploy/deployBridge.ts: -------------------------------------------------------------------------------- 1 | import * as aptos from "aptos" 2 | import * as layerzero from "../../src" 3 | import * as path from "path" 4 | import { BRIDGE_MODULES, compilePackage, getMetadataAndModules } from "../utils" 5 | import { FAUCET_URL, NODE_URL } from "../../src/constants" 6 | import { Environment } from "../../src/types" 7 | import { ChainStage } from "@layerzerolabs/core-sdk" 8 | 9 | export async function deployBridge( 10 | env: Environment, 11 | stage: ChainStage, 12 | account: aptos.AptosAccount, 13 | layerzeroAddress: string = undefined, 14 | ) { 15 | const bridgeAddress = account.address().toString() 16 | console.log({ 17 | env, 18 | stage, 19 | bridgeAddress, 20 | }) 21 | 22 | if (env === Environment.LOCAL) { 23 | const faucet = new aptos.FaucetClient(NODE_URL[env], FAUCET_URL[env]) 24 | await faucet.fundAccount(bridgeAddress, 1000000000) 25 | } 26 | 27 | const sdk = new layerzero.SDK({ 28 | provider: new aptos.AptosClient(NODE_URL[env]), 29 | stage, 30 | }) 31 | 32 | // compile and deploy bridge 33 | const packagePath = path.join(__dirname, "../../../apps/bridge") 34 | await compilePackage(packagePath, packagePath, { 35 | layerzero: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 36 | layerzero_common: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 37 | msglib_auth: layerzeroAddress ?? sdk.accounts.msglib_auth.toString(), 38 | zro: layerzeroAddress ?? sdk.accounts.zro.toString(), 39 | msglib_v1_1: layerzeroAddress ?? sdk.accounts.msglib_v1_1.toString(), 40 | msglib_v2: layerzeroAddress ?? sdk.accounts.msglib_v2.toString(), 41 | executor_auth: layerzeroAddress ?? sdk.accounts.executor_auth.toString(), 42 | executor_v2: layerzeroAddress ?? sdk.accounts.executor_v2.toString(), 43 | bridge: bridgeAddress, 44 | }) 45 | 46 | const { metadata, modules } = getMetadataAndModules(packagePath, BRIDGE_MODULES) 47 | await sdk.deploy(account, metadata, modules) 48 | 49 | console.log("Deployed Bridge!!") 50 | } 51 | -------------------------------------------------------------------------------- /sdk/tasks/deploy/deployCounter.ts: -------------------------------------------------------------------------------- 1 | import { FAUCET_URL, NODE_URL } from "../../src/constants" 2 | import * as aptos from "aptos" 3 | import * as layerzero from "../../src" 4 | import * as path from "path" 5 | import { compilePackage, COUNTER_MODULES, getMetadataAndModules } from "../utils" 6 | import { Environment } from "../../src/types" 7 | import { ChainStage } from "@layerzerolabs/core-sdk" 8 | 9 | export async function deployCounter( 10 | env: Environment, 11 | stage: ChainStage, 12 | account: aptos.AptosAccount, 13 | layerzeroAddress: string = undefined, 14 | ) { 15 | const counterAddress = account.address().toString() 16 | console.log({ 17 | env, 18 | stage, 19 | counterAddress, 20 | }) 21 | 22 | if (env === Environment.LOCAL) { 23 | const faucet = new aptos.FaucetClient(NODE_URL[env], FAUCET_URL[env]) 24 | await faucet.fundAccount(counterAddress, 1000000000) 25 | } 26 | 27 | const sdk = new layerzero.SDK({ 28 | provider: new aptos.AptosClient(NODE_URL[env]), 29 | stage, 30 | }) 31 | 32 | // compile and deploy bridge 33 | const packagePath = path.join(__dirname, "../../../apps/counter") 34 | await compilePackage(packagePath, packagePath, { 35 | layerzero: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 36 | layerzero_common: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 37 | msglib_auth: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 38 | zro: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 39 | msglib_v1_1: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 40 | msglib_v2: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 41 | executor_auth: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 42 | executor_v2: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 43 | counter: counterAddress, 44 | }) 45 | 46 | const { metadata, modules } = getMetadataAndModules(packagePath, COUNTER_MODULES) 47 | await sdk.deploy(account, metadata, modules) 48 | 49 | console.log("Deployed Counter!!") 50 | } 51 | -------------------------------------------------------------------------------- /apps/bridge-evm/contracts/mocks/WETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BUSL-1.1 2 | 3 | pragma solidity >=0.4.22; 4 | 5 | contract WETH { 6 | string public name = "Wrapped Ether"; 7 | string public symbol = "WETH"; 8 | uint8 public decimals = 18; 9 | 10 | event Approval(address indexed src, address indexed guy, uint wad); 11 | event Transfer(address indexed src, address indexed dst, uint wad); 12 | event Deposit(address indexed dst, uint wad); 13 | event Withdrawal(address indexed src, uint wad); 14 | 15 | mapping(address => uint) public balanceOf; 16 | mapping(address => mapping(address => uint)) public allowance; 17 | 18 | receive() external payable { 19 | deposit(); 20 | } 21 | 22 | function deposit() public payable { 23 | balanceOf[msg.sender] += msg.value; 24 | emit Deposit(msg.sender, msg.value); 25 | } 26 | 27 | function withdraw(uint wad) public { 28 | require(balanceOf[msg.sender] >= wad); 29 | balanceOf[msg.sender] -= wad; 30 | payable(msg.sender).transfer(wad); 31 | emit Withdrawal(msg.sender, wad); 32 | } 33 | 34 | function totalSupply() public view returns (uint) { 35 | return address(this).balance; 36 | } 37 | 38 | function approve(address guy, uint wad) public returns (bool) { 39 | allowance[msg.sender][guy] = wad; 40 | emit Approval(msg.sender, guy, wad); 41 | return true; 42 | } 43 | 44 | function transfer(address dst, uint wad) public returns (bool) { 45 | return transferFrom(msg.sender, dst, wad); 46 | } 47 | 48 | function transferFrom( 49 | address src, 50 | address dst, 51 | uint wad 52 | ) public returns (bool) { 53 | require(balanceOf[src] >= wad); 54 | 55 | if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) { 56 | require(allowance[src][msg.sender] >= wad); 57 | allowance[src][msg.sender] -= wad; 58 | } 59 | 60 | balanceOf[src] -= wad; 61 | balanceOf[dst] += wad; 62 | 63 | emit Transfer(src, dst, wad); 64 | 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /layerzero-common/sources/utils.move: -------------------------------------------------------------------------------- 1 | module layerzero_common::utils { 2 | use std::vector; 3 | use std::error; 4 | use std::signer::address_of; 5 | use aptos_std::type_info::{account_address, type_of}; 6 | 7 | const ELAYERZERO_INVALID_INDEX: u64 = 0x00; 8 | const ELAYERZERO_INVALID_U16: u64 = 0x01; 9 | const ELAYERZERO_INVALID_LENGTH: u64 = 0x02; 10 | const ELAYERZERO_PERMISSION_DENIED: u64 = 0x03; 11 | 12 | public fun vector_slice(vec: &vector, start: u64, end: u64): vector { 13 | assert!(start < end && end <= vector::length(vec), error::invalid_argument(ELAYERZERO_INVALID_INDEX)); 14 | let slice = vector::empty(); 15 | let i = start; 16 | while (i < end) { 17 | vector::push_back(&mut slice, *vector::borrow(vec, i)); 18 | i = i + 1; 19 | }; 20 | slice 21 | } 22 | 23 | public fun assert_signer(account: &signer, account_address: address) { 24 | assert!(address_of(account) == account_address, error::permission_denied(ELAYERZERO_PERMISSION_DENIED)); 25 | } 26 | 27 | public fun assert_length(data: &vector, length: u64) { 28 | assert!(vector::length(data) == length, error::invalid_argument(ELAYERZERO_INVALID_LENGTH)); 29 | } 30 | 31 | public fun assert_type_signer(account: &signer) { 32 | assert!(type_address() == address_of(account), error::permission_denied(ELAYERZERO_PERMISSION_DENIED)); 33 | } 34 | 35 | public fun assert_u16(chain_id: u64) { 36 | assert!(chain_id <= 65535, error::invalid_argument(ELAYERZERO_INVALID_U16)); 37 | } 38 | 39 | public fun type_address(): address { 40 | account_address(&type_of()) 41 | } 42 | 43 | #[test] 44 | fun test_vector_slice() { 45 | let vec = vector[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 46 | let slice = vector_slice(&vec, 2, 8); 47 | assert!(slice == vector[3, 4, 5, 6, 7, 8], 0); 48 | 49 | let slice = vector_slice(&vec, 2, 3); 50 | assert!(slice == vector[3], 0); 51 | } 52 | 53 | #[test] 54 | #[expected_failure(abort_code = 0x10000, location = Self)] 55 | fun test_vector_slice_with_invalid_index() { 56 | let vec = vector[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 57 | vector_slice(&vec, 2, 20); 58 | } 59 | } -------------------------------------------------------------------------------- /sdk/tests/bcs.test.ts: -------------------------------------------------------------------------------- 1 | import * as aptos from "aptos" 2 | 3 | export function convertToPaddedUint8Array(str: string, length: number): Uint8Array { 4 | const value = Uint8Array.from(Buffer.from(aptos.HexString.ensure(str).noPrefix().padStart(length, "0"), "hex")) 5 | return Uint8Array.from([...Array.from(new Uint8Array(length - value.length)), ...Array.from(value)]) 6 | } 7 | 8 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 9 | function argToBytes(argVal: any, argType: aptos.TxnBuilderTypes.TypeTag) { 10 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 11 | return (aptos.TransactionBuilderABI as any).toBCSArgs([{ type_tag: argType }], [argVal])[0] 12 | } 13 | 14 | describe("argument of vector", () => { 15 | const typeTag = new aptos.TypeTagParser("vector").parseTypeTag() 16 | const value = convertToPaddedUint8Array("6d9f1a927cbcb5e2c28d13ca735bc6d6131406da", 32) 17 | 18 | test("Uint8Array value", () => { 19 | const val = value 20 | const bytes = argToBytes(val, typeTag) 21 | expect(bytes.length).toBe(value.length + 1) 22 | expect(bytes.slice(1)).toEqual(value) 23 | }) 24 | 25 | test("Uint8Array value serialized as JSON object", () => { 26 | const val = JSON.parse(JSON.stringify(value)) 27 | expect(() => { 28 | argToBytes(val, typeTag) 29 | }).toThrowError("Invalid vector args.") 30 | }) 31 | 32 | test("string value", () => { 33 | const val = Buffer.from(value).toString() 34 | const bytes = argToBytes(val, typeTag) 35 | expect(bytes.length).not.toBe(value.length + 1) 36 | const raw = bytes.slice(1) 37 | const encoded = Buffer.from(raw).toString("hex") 38 | expect(encoded).toBe( 39 | "0000000000000000000000006defbfbd1aefbfbd7cefbfbdefbfbdefbfbdc28d13efbfbd735befbfbdefbfbd131406efbfbd", 40 | ) 41 | }) 42 | 43 | test("hex string value", () => { 44 | const val = Buffer.from(value).toString("hex") 45 | const bytes = argToBytes(val, typeTag) 46 | expect(bytes.length).not.toBe(value.length + 1) 47 | const raw = bytes.slice(1) 48 | const encoded = Buffer.from(raw).toString("hex") 49 | expect(encoded).toBe( 50 | "30303030303030303030303030303030303030303030303036643966316139323763626362356532633238643133636137333562633664363133313430366461", 51 | ) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /sdk/tasks/deploy/deployLayerzero.ts: -------------------------------------------------------------------------------- 1 | import { FAUCET_URL, NODE_URL } from "../../src/constants" 2 | import * as aptos from "aptos" 3 | import * as layerzero from "../../src" 4 | import * as path from "path" 5 | import { compilePackage, getMetadataAndModules, initialDeploy, LAYERZERO_MODULES } from "../utils" 6 | import { Environment } from "../../src/types" 7 | 8 | export async function deployLayerzero(env: Environment, endpointId: number, account: aptos.AptosAccount) { 9 | const layerzeroAddress = account.address().toString() 10 | console.log({ 11 | env, 12 | endpointId, 13 | layerzeroAddress, 14 | }) 15 | 16 | if (env === Environment.LOCAL) { 17 | const faucet = new aptos.FaucetClient(NODE_URL[env], FAUCET_URL[env]) 18 | await faucet.fundAccount(layerzeroAddress, 1000000000) 19 | } 20 | 21 | const sdk = new layerzero.SDK({ 22 | provider: new aptos.AptosClient(NODE_URL[env]), 23 | accounts: { 24 | layerzero: layerzeroAddress, 25 | msglib_auth: layerzeroAddress, 26 | msglib_v1_1: layerzeroAddress, 27 | msglib_v2: layerzeroAddress, 28 | zro: layerzeroAddress, 29 | executor_auth: layerzeroAddress, 30 | executor_v2: layerzeroAddress, 31 | }, 32 | }) 33 | 34 | // compile and deploy layerzero 35 | const packagePath = path.join(__dirname, "../../../layerzero") 36 | await compilePackage(packagePath, packagePath, { 37 | layerzero: layerzeroAddress, 38 | layerzero_common: layerzeroAddress, 39 | msglib_auth: layerzeroAddress, 40 | executor_auth: layerzeroAddress, 41 | zro: layerzeroAddress, 42 | msglib_v1_1: layerzeroAddress, 43 | msglib_v2: layerzeroAddress, 44 | executor_v2: layerzeroAddress, 45 | }) 46 | const initial = await initialDeploy(sdk.client, account.address(), LAYERZERO_MODULES) 47 | const { metadata, modules } = getMetadataAndModules(packagePath, LAYERZERO_MODULES) 48 | await sdk.deploy(account, metadata, modules) 49 | 50 | if (initial) { 51 | console.log("Initial deploy: configuring MsglibAuth") 52 | await sdk.LayerzeroModule.Endpoint.initialize(account, endpointId) 53 | await sdk.LayerzeroModule.Endpoint.registerExecutor(account, sdk.LayerzeroModule.Executor.type) 54 | await sdk.LayerzeroModule.Uln.Receive.initialize(account) 55 | } 56 | 57 | console.log("Deployed Layerzero!!") 58 | } 59 | -------------------------------------------------------------------------------- /layerzero/sources/admin.move: -------------------------------------------------------------------------------- 1 | // managing layerzero admin priviledges. 2 | // can transfer the admin account to new multi-sig or resources accounts 3 | module layerzero::admin { 4 | use std::error; 5 | use std::signer::address_of; 6 | 7 | const EADMIN_NOT_AUTHORIZED: u64 = 0x00; 8 | 9 | struct Config has key { 10 | admin: address 11 | } 12 | 13 | // defaults to @layerzero 14 | fun init_module(account: &signer) { 15 | move_to(account, Config { admin: @layerzero } ) 16 | } 17 | 18 | public entry fun transfer_admin(account: &signer, new_admin: address) acquires Config { 19 | let config = borrow_global_mut(@layerzero); 20 | assert!( 21 | address_of(account) == config.admin, 22 | error::permission_denied(EADMIN_NOT_AUTHORIZED) 23 | ); 24 | 25 | config.admin = new_admin; 26 | } 27 | 28 | public fun is_config_admin(account: address): bool acquires Config { 29 | let config = borrow_global(@layerzero); 30 | account == config.admin 31 | } 32 | 33 | public fun assert_config_admin(account: &signer) acquires Config { 34 | assert!( 35 | is_config_admin(address_of(account)), 36 | error::permission_denied(EADMIN_NOT_AUTHORIZED) 37 | ); 38 | } 39 | 40 | #[test_only] 41 | public fun init_module_for_test(account: &signer) { 42 | init_module(account); 43 | } 44 | 45 | #[test(lz = @layerzero, alice = @1234)] 46 | #[expected_failure(abort_code = 0x50000)] 47 | fun test_set_by_non_admin(lz: &signer, alice: &signer) acquires Config { 48 | init_module(lz); 49 | 50 | transfer_admin(alice, address_of(lz)) 51 | } 52 | 53 | #[test(lz = @layerzero, alice = @1234)] 54 | fun test_set_by_admin(lz: &signer, alice: &signer) acquires Config { 55 | init_module(lz); 56 | 57 | let alice_addr = address_of(alice); 58 | transfer_admin(lz, alice_addr); 59 | 60 | let config = borrow_global(@layerzero); 61 | assert!(config.admin == alice_addr, 0); 62 | } 63 | 64 | #[test(lz = @layerzero, alice = @1234)] 65 | #[expected_failure(abort_code = 0x50000)] 66 | fun test_assert_not_admin(lz: &signer, alice: &signer) acquires Config { 67 | init_module(lz); 68 | 69 | assert_config_admin(alice); 70 | } 71 | 72 | #[test(lz = @layerzero)] 73 | fun test_assert_admin(lz: &signer) acquires Config { 74 | init_module(lz); 75 | 76 | assert_config_admin(lz); 77 | } 78 | } -------------------------------------------------------------------------------- /sdk/src/modules/executor_config.ts: -------------------------------------------------------------------------------- 1 | import { SDK } from "../index" 2 | import * as aptos from "aptos" 3 | import { isErrorOfApiError } from "../utils" 4 | 5 | export class ExecutorConfig { 6 | public readonly module 7 | 8 | constructor(private sdk: SDK) { 9 | this.module = `${sdk.accounts.layerzero}::executor_config` 10 | } 11 | 12 | async getDefaultExecutor(remoteChainId: aptos.BCS.Uint16): Promise<[string, aptos.BCS.Uint64]> { 13 | const resource = await this.sdk.client.getAccountResource( 14 | this.sdk.accounts.layerzero!, 15 | `${this.module}::ConfigStore`, 16 | ) 17 | 18 | const { config } = resource.data as { config: { handle: string } } 19 | try { 20 | const response = await this.sdk.client.getTableItem(config.handle, { 21 | key_type: "u64", 22 | value_type: `${this.module}::Config`, 23 | key: remoteChainId.toString(), 24 | }) 25 | return [response.executor, response.version] 26 | } catch (e) { 27 | if (isErrorOfApiError(e, 404)) { 28 | return ["", BigInt(0)] 29 | } 30 | throw e 31 | } 32 | } 33 | 34 | async getExecutor( 35 | uaAddress: aptos.MaybeHexString, 36 | remoteChainId: aptos.BCS.Uint16, 37 | ): Promise<[string, aptos.BCS.Uint64]> { 38 | const resource = await this.sdk.client.getAccountResource(uaAddress, `${this.module}::ConfigStore`) 39 | const { config } = resource.data as { config: { handle: string } } 40 | 41 | try { 42 | const response = await this.sdk.client.getTableItem(config.handle, { 43 | key_type: "u64", 44 | value_type: `${this.module}::Config`, 45 | key: remoteChainId.toString(), 46 | }) 47 | return [response.executor, response.version] 48 | } catch (e) { 49 | if (isErrorOfApiError(e, 404)) { 50 | return await this.getDefaultExecutor(remoteChainId) 51 | } 52 | throw e 53 | } 54 | } 55 | 56 | setDefaultExecutorPayload( 57 | remoteChainId: aptos.BCS.Uint16, 58 | version: aptos.BCS.Uint8, 59 | executor: aptos.MaybeHexString, 60 | ): aptos.Types.EntryFunctionPayload { 61 | return { 62 | function: `${this.module}::set_default_executor`, 63 | type_arguments: [], 64 | arguments: [remoteChainId, version, executor], 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | **/.aptos/ 3 | **/coverage/ 4 | .DS_Store 5 | 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # TypeScript v1 declaration files 50 | typings/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Microbundle cache 62 | .rpt2_cache/ 63 | .rts2_cache_cjs/ 64 | .rts2_cache_es/ 65 | .rts2_cache_umd/ 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variables file 77 | .env 78 | .env.test 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | 83 | # Next.js build output 84 | .next 85 | 86 | # Nuxt.js build / generate output 87 | .nuxt 88 | dist 89 | 90 | # Gatsby files 91 | .cache/ 92 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 93 | # https://nextjs.org/blog/next-9-1#public-directory-support 94 | # public 95 | 96 | # vuepress build output 97 | .vuepress/dist 98 | 99 | # Serverless directories 100 | .serverless/ 101 | 102 | # FuseBox cache 103 | .fusebox/ 104 | 105 | # DynamoDB Local files 106 | .dynamodb/ 107 | 108 | # TernJS port file 109 | .tern-port 110 | 111 | #Hardhat files 112 | artifacts/ 113 | hh-cache/ 114 | 115 | #foundry test compilation files 116 | cache 117 | out 118 | reference-out 119 | optimized-out 120 | reference-working 121 | 122 | # local ide 123 | .idea/ 124 | .vscode/ 125 | 126 | **/coverage.json 127 | **/deployments/localhost* 128 | **/deployments/*-fork/ 129 | transactions.csv -------------------------------------------------------------------------------- /sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@layerzerolabs/lz-aptos", 3 | "version": "1.0.2", 4 | "description": "LayerZero Aptos Javascript API", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "directories": { 8 | "test": "tests" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/LayerZero-Labs/LayerZero-Aptos-Contract.git" 13 | }, 14 | "keywords": [ 15 | "LayerZero", 16 | "Aptos" 17 | ], 18 | "bugs": { 19 | "url": "https://github.com/LayerZero-Labs/LayerZero-Aptos-Contract/issues" 20 | }, 21 | "homepage": "https://github.com/LayerZero-Labs/LayerZero-Aptos-Contract#readme", 22 | "files": [ 23 | "./dist/**/*" 24 | ], 25 | "bin": { 26 | "lz-aptos-cli": "./cmd/lz-aptos-cli.ts" 27 | }, 28 | "scripts": { 29 | "build": "rimraf dist && tsc -p tsconfig.json", 30 | "clean": "rimraf dist", 31 | "format": "prettier --write \"./**/*.{ts,js,json,md}\"", 32 | "format-check": "prettier --check \"./**/*.{ts,js,json,md}\"", 33 | "lint": "eslint --ext .ts,.tsx src tests", 34 | "lint:fix": "eslint --ext .ts,.tsx src tests --fix" 35 | }, 36 | "devDependencies": { 37 | "@babel/core": "^7.18.9", 38 | "@babel/preset-env": "^7.18.9", 39 | "@babel/preset-typescript": "^7.18.6", 40 | "@types/expand-tilde": "^2.0.0", 41 | "@types/glob": "^7.2.0", 42 | "@types/globby": "^9.1.0", 43 | "@types/jest": "^28.1.6", 44 | "@typescript-eslint/eslint-plugin": "^5.37.0", 45 | "@typescript-eslint/parser": "^5.37.0", 46 | "babel-cli": "^6.26.0", 47 | "babel-jest": "^28.1.3", 48 | "eslint": "^8.23.1", 49 | "eslint-config-prettier": "^8.5.0", 50 | "eslint-plugin-unused-imports": "^2.0.0", 51 | "jest": "^28.1.3", 52 | "jest-serial-runner": "^1.2.0", 53 | "postinstall-postinstall": "^2.1.0", 54 | "prettier": "^2.7.1", 55 | "rimraf": "^3.0.2", 56 | "ts-jest": "^28.0.7", 57 | "typescript": "^4.7.4" 58 | }, 59 | "dependencies": { 60 | "@layerzerolabs/core-sdk": "^1.0.41-beta.37", 61 | "@noble/hashes": "^1.1.2", 62 | "@types/bn.js": "^5.1.0", 63 | "@types/command-line-args": "^5.2.0", 64 | "@types/command-line-usage": "^5.0.2", 65 | "@types/node": "^18.8.2", 66 | "aptos": "^1.3.14", 67 | "bip39": "^3.0.4", 68 | "bn.js": "^5.2.1", 69 | "cli-ux": "^6.0.9", 70 | "command-line-args": "^5.2.1", 71 | "command-line-usage": "^6.1.3", 72 | "commander": "^9.4.0", 73 | "console-table-printer": "^2.11.1", 74 | "cross-fetch": "^3.1.5", 75 | "ethers": "^5.7.1", 76 | "expand-tilde": "^2.0.2", 77 | "extended-buffer": "^6.1.0", 78 | "glob": "^8.0.3", 79 | "tiny-invariant": "^1.2.0", 80 | "ts-node": "^10.9.1", 81 | "tweetnacl": "^1.0.3" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /sdk/tasks/deploy/deployOracle.ts: -------------------------------------------------------------------------------- 1 | import { FAUCET_URL, NODE_URL } from "../../src/constants" 2 | import * as aptos from "aptos" 3 | import * as layerzero from "../../src" 4 | import * as path from "path" 5 | import * as fs from "fs" 6 | import { Oracle } from "../../src/modules/apps/oracle" 7 | import { compilePackage, ORACLE_MODULES } from "../utils" 8 | import { Environment } from "../../src/types" 9 | import { ChainStage } from "@layerzerolabs/core-sdk" 10 | 11 | export async function deployOracle( 12 | env: Environment, 13 | stage: ChainStage, 14 | account: aptos.AptosAccount, 15 | layerzeroAddress: string = undefined, 16 | ) { 17 | const oracleAddress = account.address().toString() 18 | console.log({ 19 | env, 20 | stage, 21 | oracleAddress, 22 | }) 23 | 24 | if (env === Environment.LOCAL) { 25 | const faucet = new aptos.FaucetClient(NODE_URL[env], FAUCET_URL[env]) 26 | await faucet.fundAccount(oracleAddress, 1000000000) 27 | } 28 | console.log(`oracle account: ${oracleAddress}`) 29 | 30 | const sdk = new layerzero.SDK({ 31 | provider: new aptos.AptosClient(NODE_URL[env]), 32 | stage: stage, 33 | }) 34 | 35 | // compile and deploy bridge 36 | const packagePath = path.join(__dirname, "../../../apps/oracle") 37 | const buildPath = path.join(__dirname, "../../../apps/oracle") 38 | const metadataPath = path.join(buildPath, "build/oracle/package-metadata.bcs") 39 | const modulePath = path.join(buildPath, "build/oracle/bytecode_modules") 40 | 41 | await compilePackage(packagePath, buildPath, { 42 | layerzero: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 43 | layerzero_common: layerzeroAddress ?? sdk.accounts.layerzero.toString(), 44 | msglib_auth: layerzeroAddress ?? sdk.accounts.msglib_auth.toString(), 45 | zro: layerzeroAddress ?? sdk.accounts.zro.toString(), 46 | msglib_v1_1: layerzeroAddress ?? sdk.accounts.msglib_v1_1.toString(), 47 | msglib_v2: layerzeroAddress ?? sdk.accounts.msglib_v2.toString(), 48 | executor_auth: layerzeroAddress ?? sdk.accounts.executor_auth.toString(), 49 | executor_v2: layerzeroAddress ?? sdk.accounts.executor_v2.toString(), 50 | oracle: oracleAddress, 51 | }) 52 | 53 | const metadata = Uint8Array.from(fs.readFileSync(metadataPath)) 54 | const modules = ORACLE_MODULES.map( 55 | (f) => new aptos.TxnBuilderTypes.Module(Uint8Array.from(fs.readFileSync(path.join(modulePath, f)))), 56 | ) 57 | await sdk.deploy(account, metadata, modules) 58 | 59 | console.log("Deployed Oracle!!") 60 | 61 | const oracleModule = new Oracle(sdk, oracleAddress) 62 | const resourceAddress = await oracleModule.getResourceAddress() 63 | console.log(`Oracle resource address: ${resourceAddress}`) 64 | } 65 | -------------------------------------------------------------------------------- /sdk/tasks/deploy/deployCommon.ts: -------------------------------------------------------------------------------- 1 | import { FAUCET_URL, NODE_URL } from "../../src/constants" 2 | import * as aptos from "aptos" 3 | import * as layerzero from "../../src" 4 | import * as path from "path" 5 | import { 6 | compilePackage, 7 | EXECUTOR_AUTH_MODULES, 8 | getMetadataAndModules, 9 | initialDeploy, 10 | LAYERZERO_COMMON_MODULES, 11 | MSGLIB_AUTH_MODULES, 12 | } from "../utils" 13 | import { Environment } from "../../src/types" 14 | 15 | export async function deployCommon(env: Environment, account: aptos.AptosAccount) { 16 | const layerzeroAddress = account.address().toString() 17 | console.log({ 18 | env, 19 | address: layerzeroAddress, 20 | }) 21 | 22 | if (env === Environment.LOCAL) { 23 | const faucet = new aptos.FaucetClient(NODE_URL[env], FAUCET_URL[env]) 24 | await faucet.fundAccount(layerzeroAddress, 1000000000) 25 | } 26 | 27 | const sdk = new layerzero.SDK({ 28 | provider: new aptos.AptosClient(NODE_URL[env]), 29 | accounts: { 30 | msglib_auth: layerzeroAddress, 31 | }, 32 | }) 33 | 34 | // compile and deploy layerzero common 35 | { 36 | const packagePath = path.join(__dirname, "../../../layerzero-common") 37 | await compilePackage(packagePath, packagePath, { 38 | layerzero_common: layerzeroAddress, 39 | }) 40 | const { metadata, modules } = getMetadataAndModules(packagePath, LAYERZERO_COMMON_MODULES) 41 | await sdk.deploy(account, metadata, modules) 42 | console.log("Deployed Layerzero Common!!") 43 | } 44 | 45 | // compile and deploy msglib auth 46 | { 47 | const packagePath = path.join(__dirname, "../../../msglib/msglib-auth") 48 | await compilePackage(packagePath, packagePath, { 49 | layerzero_common: layerzeroAddress, 50 | msglib_auth: layerzeroAddress, 51 | }) 52 | const initial = await initialDeploy(sdk.client, account.address(), MSGLIB_AUTH_MODULES) 53 | const { metadata, modules } = getMetadataAndModules(packagePath, MSGLIB_AUTH_MODULES) 54 | await sdk.deploy(account, metadata, modules) 55 | 56 | if (initial) { 57 | console.log("Initial deploy: configuring MsglibAuth") 58 | await sdk.LayerzeroModule.MsgLibAuth.allow(account, layerzeroAddress) 59 | } 60 | 61 | console.log("Deployed Msglib Auth!!") 62 | } 63 | 64 | // compile and deploy executor auth 65 | { 66 | const packagePath = path.join(__dirname, "../../../executor/executor-auth") 67 | await compilePackage(packagePath, packagePath, { 68 | layerzero_common: layerzeroAddress, 69 | executor_auth: layerzeroAddress, 70 | }) 71 | const { metadata, modules } = getMetadataAndModules(packagePath, EXECUTOR_AUTH_MODULES) 72 | await sdk.deploy(account, metadata, modules) 73 | console.log("Deployed Executor Auth!!") 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /layerzero/sources/app/lzapp/remote.move: -------------------------------------------------------------------------------- 1 | /// LZApp Remote module helps the app manage its trusted remote addresses on other chains, 2 | /// where the app only wants to send msg to and receive msg from. 3 | /// It only supports that there is only one trusted remote address on each chain. 4 | /// 5 | /// Remote is saparated from the lzApp because lzApp might have multiple remotes 6 | module layerzero::remote { 7 | use std::error; 8 | use aptos_std::table::{Self, Table}; 9 | use layerzero_common::utils::{type_address, assert_u16}; 10 | use std::signer::address_of; 11 | use layerzero::endpoint::UaCapability; 12 | 13 | const ELZAPP_REMOTE_ALREADY_INITIALIZED: u64 = 0x00; 14 | const ELZAPP_REMOTE_NOT_INITIALIZED: u64 = 0x01; 15 | const ELZAPP_INVALID_REMOTE: u64 = 0x02; 16 | 17 | struct Remotes has key { 18 | // chainId -> remote address 19 | peers: Table> 20 | } 21 | 22 | public fun init(account: &signer) { 23 | assert!(!exists(address_of(account)), error::already_exists(ELZAPP_REMOTE_ALREADY_INITIALIZED)); 24 | 25 | move_to(account, Remotes { 26 | peers: table::new(), 27 | }); 28 | } 29 | 30 | /// Set a trusted remote address for a chain by an admin signer. 31 | public entry fun set(account: &signer, chain_id: u64, remote_addr: vector) acquires Remotes { 32 | set_internal(address_of(account), chain_id, remote_addr); 33 | } 34 | 35 | /// Set a trusted remote address for a chain with UaCapability. 36 | public fun set_with_cap(chain_id: u64, remote_addr: vector, _cap: &UaCapability) acquires Remotes { 37 | set_internal(type_address(), chain_id, remote_addr); 38 | } 39 | 40 | fun set_internal(ua_address: address, chain_id: u64, remote_addr: vector) acquires Remotes { 41 | assert_u16(chain_id); 42 | assert!(exists(ua_address), error::not_found(ELZAPP_REMOTE_NOT_INITIALIZED)); 43 | 44 | let remotes = borrow_global_mut(ua_address); 45 | table::upsert(&mut remotes.peers, chain_id, remote_addr) 46 | } 47 | 48 | public fun get(ua_address: address, chain_id: u64): vector acquires Remotes { 49 | assert!(exists(ua_address), error::not_found(ELZAPP_REMOTE_NOT_INITIALIZED)); 50 | 51 | let remotes = borrow_global(ua_address); 52 | *table::borrow(&remotes.peers, chain_id) 53 | } 54 | 55 | public fun contains(ua_address: address, chain_id: u64): bool acquires Remotes { 56 | assert!(exists(ua_address), error::not_found(ELZAPP_REMOTE_NOT_INITIALIZED)); 57 | 58 | let remotes = borrow_global(ua_address); 59 | table::contains(&remotes.peers, chain_id) 60 | } 61 | 62 | public fun assert_remote(ua_address: address, chain_id: u64, remote_addr: vector) acquires Remotes { 63 | let expected = get(ua_address, chain_id); 64 | assert!(expected == remote_addr, error::invalid_argument(ELZAPP_INVALID_REMOTE)); 65 | } 66 | } -------------------------------------------------------------------------------- /sdk/src/constants.ts: -------------------------------------------------------------------------------- 1 | import { ChainStage } from "@layerzerolabs/core-sdk" 2 | import { Environment } from "./types" 3 | 4 | export const NODE_URL: { [env in Environment]: string } = { 5 | [Environment.MAINNET]: "https://mainnet.aptoslabs.com/v1", 6 | [Environment.TESTNET]: "https://fullnode.testnet.aptoslabs.com/v1", 7 | [Environment.DEVNET]: "https://fullnode.devnet.aptoslabs.com/v1", 8 | [Environment.LOCAL]: "http://127.0.0.1:8080/v1", 9 | } 10 | 11 | export const FAUCET_URL: { [env in Environment]: string } = { 12 | [Environment.MAINNET]: "", 13 | [Environment.TESTNET]: "https://faucet.testnet.aptoslabs.com", 14 | [Environment.DEVNET]: "https://faucet.devnet.aptoslabs.com", 15 | [Environment.LOCAL]: "http://127.0.0.1:8081", 16 | } 17 | 18 | export const LAYERZERO_ADDRESS: { [stage in ChainStage]?: string } = { 19 | [ChainStage.MAINNET]: "0x54ad3d30af77b60d939ae356e6606de9a4da67583f02b962d2d3f2e481484e90", 20 | [ChainStage.TESTNET]: "0x1759cc0d3161f1eb79f65847d4feb9d1f74fb79014698a23b16b28b9cd4c37e3", 21 | [ChainStage.TESTNET_SANDBOX]: "0xcdc2c5597e2a96faf08135db560e3846e8c8c5683b0db868f6ad68f143906b3e", 22 | } 23 | 24 | export const ORACLE_ADDRESS: { [stage in ChainStage]?: string } = { 25 | [ChainStage.MAINNET]: "0xc2846ea05319c339b3b52186ceae40b43d4e9cf6c7350336c3eb0b351d9394eb", 26 | [ChainStage.TESTNET]: "0x8ab85d94bf34808386b3ce0f9516db74d2b6d2f1166aa48f75ca641f3adb6c63", 27 | [ChainStage.TESTNET_SANDBOX]: "0x38ee7e8bc9d2601ec0934a5d6e23182a266380d87840e5f0850bfeb647297d3a", 28 | } 29 | 30 | export const ORACLE_SIGNER_ADDRESS: { [stage in ChainStage]?: string } = { 31 | [ChainStage.MAINNET]: "0x12e12de0af996d9611b0b78928cd9f4cbf50d94d972043cdd829baa77a78929b", 32 | [ChainStage.TESTNET]: "0x47a30bcdb5b5bdbf6af883c7325827f3e40b3f52c3538e9e677e68cf0c0db060", 33 | [ChainStage.TESTNET_SANDBOX]: "0x760b1ad2811b7c3e7e04a9dc38520320dc30850fbf001db61c18d1e36221d5c8", 34 | } 35 | 36 | export const RELAYER_SIGNER_ADDRESS: { [stage in ChainStage]?: string } = { 37 | [ChainStage.MAINNET]: "0x1d8727df513fa2a8785d0834e40b34223daff1affc079574082baadb74b66ee4", 38 | [ChainStage.TESTNET]: "0xc192864c4215741051321d44f89c3b7a54840a0b1b7ef5bec6149a07f9df4641", 39 | [ChainStage.TESTNET_SANDBOX]: "0xc180500ddac3fef70cb1e9fc0d75793850e2cef84d518ea0a3b3adfb93751ea7", 40 | } 41 | 42 | export const EXECUTOR_ADDRESS: { [stage in ChainStage]?: string } = { 43 | [ChainStage.MAINNET]: "0x1d8727df513fa2a8785d0834e40b34223daff1affc079574082baadb74b66ee4", 44 | [ChainStage.TESTNET]: "0xc192864c4215741051321d44f89c3b7a54840a0b1b7ef5bec6149a07f9df4641", 45 | [ChainStage.TESTNET_SANDBOX]: "0xc180500ddac3fef70cb1e9fc0d75793850e2cef84d518ea0a3b3adfb93751ea7", 46 | } 47 | 48 | export const BRIDGE_ADDRESS: { [stage in ChainStage]?: string } = { 49 | [ChainStage.MAINNET]: "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa", 50 | [ChainStage.TESTNET]: "0xec84c05cc40950c86d8a8bed19552f1e8ebb783196bb021c916161d22dc179f7", 51 | [ChainStage.TESTNET_SANDBOX]: "0x808b4ffe04011cd20327a910518b4bff661f73fa907e9fc41ad690f84fa6f83e", 52 | } 53 | -------------------------------------------------------------------------------- /layerzero-common/sources/serde.move: -------------------------------------------------------------------------------- 1 | module layerzero_common::serde { 2 | use std::vector; 3 | use std::error; 4 | 5 | const EINVALID_LENGTH: u64 = 0x00; 6 | 7 | public fun serialize_u8(buf: &mut vector, v: u8) { 8 | vector::push_back(buf, v); 9 | } 10 | 11 | public fun serialize_u16(buf: &mut vector, v: u64) { 12 | assert!(v <= 65535, error::invalid_argument(EINVALID_LENGTH)); 13 | serialize_u8(buf, (((v >> 8) & 0xFF) as u8)); 14 | serialize_u8(buf, ((v & 0xFF) as u8)); 15 | } 16 | 17 | public fun serialize_u64(buf: &mut vector, v: u64) { 18 | serialize_u8(buf, (((v >> 56) & 0xFF) as u8)); 19 | serialize_u8(buf, (((v >> 48) & 0xFF) as u8)); 20 | serialize_u8(buf, (((v >> 40) & 0xFF) as u8)); 21 | serialize_u8(buf, (((v >> 32) & 0xFF) as u8)); 22 | serialize_u8(buf, (((v >> 24) & 0xFF) as u8)); 23 | serialize_u8(buf, (((v >> 16) & 0xFF) as u8)); 24 | serialize_u8(buf, (((v >> 8) & 0xFF) as u8)); 25 | serialize_u8(buf, ((v & 0xFF) as u8)); 26 | } 27 | 28 | public fun serialize_vector(buf: &mut vector, v: vector) { 29 | vector::append(buf, v); 30 | } 31 | 32 | public fun deserialize_u8(buf: &vector): u8 { 33 | assert!(vector::length(buf) == 1, error::invalid_argument(EINVALID_LENGTH)); 34 | *vector::borrow(buf, 0) 35 | } 36 | 37 | public fun deserialize_u16(buf: &vector): u64 { 38 | assert!(vector::length(buf) == 2, error::invalid_argument(EINVALID_LENGTH)); 39 | ((*vector::borrow(buf, 0) as u64) << 8) + (*vector::borrow(buf, 1) as u64) 40 | } 41 | 42 | public fun deserialize_u64(buf: &vector): u64 { 43 | assert!(vector::length(buf) == 8, error::invalid_argument(EINVALID_LENGTH)); 44 | ((*vector::borrow(buf, 0) as u64) << 56) 45 | + ((*vector::borrow(buf, 1) as u64) << 48) 46 | + ((*vector::borrow(buf, 2) as u64) << 40) 47 | + ((*vector::borrow(buf, 3) as u64) << 32) 48 | + ((*vector::borrow(buf, 4) as u64) << 24) 49 | + ((*vector::borrow(buf, 5) as u64) << 16) 50 | + ((*vector::borrow(buf, 6) as u64) << 8) 51 | + (*vector::borrow(buf, 7) as u64) 52 | } 53 | 54 | #[test] 55 | fun test_serialize() { 56 | let data = vector::empty(); 57 | serialize_u8(&mut data, 1); 58 | assert!(data == vector[1], 0); 59 | 60 | let data = vector::empty(); 61 | serialize_u16(&mut data, 258); 62 | assert!(data == vector[1, 2], 0); 63 | 64 | let data = vector::empty(); 65 | serialize_u64(&mut data, 72623859790382856); 66 | assert!(data == vector[1, 2, 3, 4, 5, 6, 7, 8], 0); 67 | } 68 | 69 | #[test] 70 | fun test_deserialize() { 71 | let data = deserialize_u8(&vector[1]); 72 | assert!(data == 1, 0); 73 | 74 | let data = deserialize_u16(&vector[1, 2]); 75 | assert!(data == 258, 0); 76 | 77 | let data = deserialize_u64(&vector[1, 2, 3, 4, 5, 6, 7, 8]); 78 | assert!(data == 72623859790382856, 0); 79 | } 80 | } -------------------------------------------------------------------------------- /apps/bridge-evm/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv" 2 | 3 | import { HardhatUserConfig } from "hardhat/config" 4 | import "@nomiclabs/hardhat-waffle" 5 | import "@nomiclabs/hardhat-ethers" 6 | import "hardhat-spdx-license-identifier" 7 | import "hardhat-gas-reporter" 8 | import "solidity-coverage" 9 | import "hardhat-deploy" 10 | import "hardhat-deploy-ethers" 11 | import "./tasks" 12 | import { ChainId, setupNetwork, setupNetworks } from "@layerzerolabs/lz-sdk" 13 | 14 | dotenv.config() 15 | 16 | //const LOCAL_IP = "127.0.0.1" 17 | const LOCAL_IP = "192.168.0.42" 18 | 19 | const config: HardhatUserConfig = { 20 | namedAccounts: { 21 | deployer: { 22 | default: 0, 23 | }, 24 | }, 25 | networks: { 26 | hardhat: { 27 | blockGasLimit: 30_000_000, 28 | throwOnCallFailures: false, 29 | }, 30 | 31 | ...setupNetwork({ url: `https://eth-mainnet.g.alchemy.com/v2/_LNG7WUztp0NiK8HSOXmxi3sVuh7ABRT` }, [ChainId.ETHEREUM]), 32 | ...setupNetwork({ url: `https://polygon-mainnet.g.alchemy.com/v2/xXH_Dx-y7cQ-v-oWjhwrbXl1m5zR1hN-` }, [ChainId.POLYGON]), 33 | ...setupNetwork({ url: `https://arb-mainnet.g.alchemy.com/v2/JVqdsumULOgSlByrFSC_DnhC7sa6DNNL` }, [ChainId.ARBITRUM]), 34 | ...setupNetwork({ url: `https://opt-mainnet.g.alchemy.com/v2/SEEFpm0O3DfDF6bh85izC24-1S2qMJqA` }, [ChainId.OPTIMISM]), 35 | ...setupNetwork({ url: `https://bsc-dataseed4.defibit.io` }, [ChainId.BSC]), 36 | ...setupNetwork({ url: `https://api.avax.network/ext/bc/C/rpc` }, [ChainId.AVALANCHE]), 37 | 38 | ...setupNetworks([[ChainId.FUJI, {}]]), 39 | ...setupNetwork( 40 | { 41 | url: "https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161", 42 | }, 43 | [ChainId.GOERLI, ChainId.GOERLI_SANDBOX] 44 | ), 45 | "ethereum-fork": { 46 | url: `http://${LOCAL_IP}:8501/`, 47 | accounts: { 48 | mnemonic: process.env.MNEMONIC ?? "test test test test test test test test test test test junk", 49 | }, 50 | }, 51 | "goerli-fork": { 52 | url: `http://${LOCAL_IP}:8501/`, 53 | accounts: { 54 | mnemonic: process.env.MNEMONIC ?? "test test test test test test test test test test test junk", 55 | }, 56 | }, 57 | "goerli-sandbox-fork": { 58 | url: `http://${LOCAL_IP}:8501/`, 59 | accounts: { 60 | mnemonic: process.env.MNEMONIC ?? "test test test test test test test test test test test junk", 61 | }, 62 | }, 63 | }, 64 | gasReporter: { 65 | enabled: process.env.REPORT_GAS !== undefined, 66 | currency: "USD", 67 | }, 68 | solidity: { 69 | compilers: [ 70 | { 71 | version: "0.8.15", 72 | settings: { 73 | optimizer: { 74 | enabled: true, 75 | runs: 1000, 76 | }, 77 | }, 78 | }, 79 | ], 80 | }, 81 | // specify separate cache for hardhat, since it could possibly conflict with foundry's 82 | paths: { cache: "hh-cache" }, 83 | spdxLicenseIdentifier: { 84 | overwrite: true, 85 | runOnCompile: true, 86 | }, 87 | } 88 | export default config 89 | -------------------------------------------------------------------------------- /sdk/src/modules/apps/coin.ts: -------------------------------------------------------------------------------- 1 | import * as aptos from "aptos" 2 | import { SDK } from "../../index" 3 | import { isErrorOfApiError } from "../../utils" 4 | import { BRIDGE_ADDRESS } from "../../constants" 5 | 6 | export enum CoinType { 7 | APTOS = "AptosCoin", // native coin 8 | // coin that bridge supports, same to bridge::coin module 9 | WETH = "WETH", 10 | WBTC = "WBTC", 11 | 12 | USDC = "USDC", 13 | USDT = "USDT", 14 | BUSD = "BUSD", 15 | USDD = "USDD", 16 | } 17 | 18 | export const supportedTypes = [CoinType.WETH, CoinType.WBTC, CoinType.USDC, CoinType.USDT, CoinType.BUSD, CoinType.USDD] 19 | 20 | export type BridgeCoinType = 21 | CoinType.WETH 22 | | CoinType.WBTC 23 | | CoinType.USDC 24 | | CoinType.USDT 25 | | CoinType.BUSD 26 | | CoinType.USDD 27 | 28 | export class Coin { 29 | private sdk: SDK 30 | private readonly bridge: aptos.MaybeHexString 31 | 32 | constructor(sdk: SDK, bridge?: aptos.MaybeHexString) { 33 | this.sdk = sdk 34 | this.bridge = bridge ?? BRIDGE_ADDRESS[sdk.stage]! 35 | } 36 | 37 | getCoinType(coin: CoinType): string { 38 | switch (coin) { 39 | case CoinType.APTOS: 40 | return `0x1::aptos_coin::${coin}` 41 | default: 42 | return `${this.bridge}::asset::${coin}` 43 | } 44 | } 45 | 46 | transferPayload( 47 | coin: CoinType, 48 | to: aptos.MaybeHexString, 49 | amount: aptos.BCS.Uint64 | aptos.BCS.Uint32, 50 | ): aptos.Types.EntryFunctionPayload { 51 | if (coin === CoinType.APTOS) { 52 | return { 53 | function: `0x1::aptos_account::transfer`, 54 | type_arguments: [], 55 | arguments: [to, amount], 56 | } 57 | } else { 58 | return { 59 | function: `0x1::coin::transfer`, 60 | type_arguments: [this.getCoinType(coin)], 61 | arguments: [to, amount], 62 | } 63 | } 64 | } 65 | 66 | async transfer( 67 | signer: aptos.AptosAccount, 68 | coin: CoinType, 69 | to: aptos.MaybeHexString, 70 | amount: aptos.BCS.Uint64 | aptos.BCS.Uint32, 71 | ): Promise { 72 | const transaction = this.transferPayload(coin, to, amount) 73 | return this.sdk.sendAndConfirmTransaction(signer, transaction) 74 | } 75 | 76 | async balance(coin: CoinType, owner: aptos.MaybeHexString): Promise { 77 | try { 78 | const resource = await this.sdk.client.getAccountResource( 79 | owner, 80 | `0x1::coin::CoinStore<${this.getCoinType(coin)}>`, 81 | ) 82 | const { coin: c } = resource.data as { coin: { value: string } } 83 | return BigInt(c.value) 84 | } catch (e) { 85 | if (isErrorOfApiError(e, 404)) { 86 | return BigInt(0) 87 | } 88 | throw e 89 | } 90 | } 91 | 92 | async isAccountRegistered(coin: CoinType, accountAddr: aptos.MaybeHexString): Promise { 93 | try { 94 | await this.sdk.client.getAccountResource(accountAddr, `0x1::coin::CoinStore<${this.getCoinType(coin)}>`) 95 | return true 96 | } catch (e) { 97 | if (isErrorOfApiError(e, 404)) { 98 | return false 99 | } 100 | throw e 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /sdk/src/modules/msglib_config.ts: -------------------------------------------------------------------------------- 1 | import { SDK } from "../index" 2 | import * as aptos from "aptos" 3 | import { isErrorOfApiError } from "../utils" 4 | 5 | export class MsgLibConfig { 6 | public readonly module 7 | public readonly moduleName 8 | public readonly semverModule 9 | 10 | constructor(private sdk: SDK) { 11 | this.module = `${sdk.accounts.layerzero}::msglib_config` 12 | this.moduleName = "layerzero::msglib_config" 13 | this.semverModule = `${sdk.accounts.layerzero}::semver` 14 | } 15 | 16 | async getDefaultSendMsgLib( 17 | remoteChainId: aptos.BCS.Uint16, 18 | ): Promise<{ major: aptos.BCS.Uint64; minor: aptos.BCS.Uint8 }> { 19 | const resource = await this.sdk.client.getAccountResource( 20 | this.sdk.accounts.layerzero!, 21 | `${this.module}::MsgLibConfig`, 22 | ) 23 | const { send_version } = resource.data as { send_version: { handle: string } } 24 | try { 25 | const response = await this.sdk.client.getTableItem(send_version.handle, { 26 | key_type: "u64", 27 | value_type: `${this.semverModule}::SemVer`, 28 | key: remoteChainId.toString(), 29 | }) 30 | return { 31 | major: BigInt(response.major), 32 | minor: Number(response.minor), 33 | } 34 | } catch (e) { 35 | if (isErrorOfApiError(e, 404)) { 36 | return { 37 | major: BigInt(0), 38 | minor: 0, 39 | } 40 | } 41 | throw e 42 | } 43 | } 44 | 45 | async getDefaultReceiveMsgLib( 46 | remoteChainId: aptos.BCS.Uint16, 47 | ): Promise<{ major: aptos.BCS.Uint64; minor: aptos.BCS.Uint8 }> { 48 | const resource = await this.sdk.client.getAccountResource( 49 | this.sdk.accounts.layerzero!, 50 | `${this.module}::MsgLibConfig`, 51 | ) 52 | const { receive_version } = resource.data as { receive_version: { handle: string } } 53 | try { 54 | const response = await this.sdk.client.getTableItem(receive_version.handle, { 55 | key_type: "u64", 56 | value_type: `${this.semverModule}::SemVer`, 57 | key: remoteChainId.toString(), 58 | }) 59 | return { 60 | major: BigInt(response.major), 61 | minor: Number(response.minor), 62 | } 63 | } catch (e) { 64 | if (isErrorOfApiError(e, 404)) { 65 | return { 66 | major: BigInt(0), 67 | minor: 0, 68 | } 69 | } 70 | throw e 71 | } 72 | } 73 | 74 | setDefaultSendMsgLibPayload( 75 | remoteChainId: aptos.BCS.Uint16, 76 | major: aptos.BCS.Uint64, 77 | minor: aptos.BCS.Uint8, 78 | ): aptos.Types.EntryFunctionPayload { 79 | return { 80 | function: `${this.module}::set_default_send_msglib`, 81 | type_arguments: [], 82 | arguments: [remoteChainId.toString(), major.toString(), minor.toString()], 83 | } 84 | } 85 | 86 | setDefaultReceiveMsgLibPayload( 87 | remoteChainId: aptos.BCS.Uint16, 88 | major: aptos.BCS.Uint64, 89 | minor: aptos.BCS.Uint8, 90 | ): aptos.Types.EntryFunctionPayload { 91 | return { 92 | function: `${this.module}::set_default_receive_msglib`, 93 | type_arguments: [], 94 | arguments: [remoteChainId.toString(), major.toString(), minor.toString()], 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /sdk/tasks/config/local.ts: -------------------------------------------------------------------------------- 1 | import { ConfigType } from "./common" 2 | import * as _ from "lodash" 3 | import { CoinType } from "../../src/modules/apps/coin" 4 | import { DEFAULT_LIMITER_CAP_SD, DEFAULT_LIMITER_WINDOW_SEC } from "../../src/modules/apps/bridge" 5 | 6 | export const CONFIG: ConfigType = { 7 | msglib: { 8 | msglibv1: { 9 | addressSize: 32, 10 | versionTypes: { 11 | "1.0": "uln_receive::ULN", 12 | }, 13 | defaultSendVersion: "1.0", 14 | defaultReceiveVersion: "1.0", 15 | }, 16 | }, 17 | endpoint: { 18 | defaultExecutorVersion: 1, 19 | defaultExecutor: { 20 | version: 1, 21 | }, 22 | }, 23 | executor: {}, 24 | relayer: {}, 25 | oracle: { 26 | threshold: 2, 27 | }, 28 | bridge: { 29 | enableCustomAdapterParams: true, 30 | coins: { 31 | [CoinType.WETH]: { 32 | name: "WETH", 33 | symbol: "WETH", 34 | decimals: 18, 35 | limiter: { 36 | enabled: true, 37 | capSD: 100000000000, 38 | windowSec: 3600, 39 | }, 40 | }, 41 | [CoinType.USDC]: { 42 | name: "USDC", 43 | symbol: "USDC", 44 | decimals: 18, 45 | limiter: { 46 | enabled: true, 47 | capSD: DEFAULT_LIMITER_CAP_SD, 48 | windowSec: DEFAULT_LIMITER_WINDOW_SEC, 49 | }, 50 | }, 51 | }, 52 | }, 53 | } 54 | 55 | export function getTestConfig( 56 | remoteChainId: number, 57 | layerzeroAddress: string, 58 | oracleAddress: string, 59 | oracleSignerAddress: string, 60 | relayerAddress: string, 61 | executorAddress: string, 62 | validators = {}, 63 | ): ConfigType { 64 | return _.merge(CONFIG, { 65 | msglib: { 66 | msglibv1: { 67 | defaultAppConfig: { 68 | [remoteChainId]: { 69 | oracle: oracleSignerAddress, 70 | relayer: relayerAddress, 71 | inboundConfirmations: 15, 72 | outboundConfirmations: 15, 73 | }, 74 | }, 75 | }, 76 | }, 77 | endpoint: { 78 | defaultExecutor: { 79 | address: executorAddress, 80 | }, 81 | defaultAdapterParam: { 82 | [remoteChainId]: { 83 | uaGas: 10000, 84 | }, 85 | }, 86 | }, 87 | executor: { 88 | address: executorAddress, 89 | fee: { 90 | [remoteChainId]: { 91 | airdropAmtCap: 10000000000, // 10^10 92 | priceRatio: 10000000000, // denominated in 10^10 93 | gasPrice: 1, 94 | }, 95 | }, 96 | }, 97 | relayer: { 98 | signerAddress: relayerAddress, 99 | fee: { 100 | [remoteChainId]: { 101 | baseFee: 100, 102 | feePerByte: 1, 103 | }, 104 | }, 105 | }, 106 | oracle: { 107 | address: oracleAddress, 108 | signerAddress: oracleSignerAddress, 109 | fee: { 110 | [remoteChainId]: { 111 | baseFee: 10, 112 | }, 113 | }, 114 | validators: validators, 115 | }, 116 | }) 117 | } 118 | -------------------------------------------------------------------------------- /msglib/msglib-auth/sources/msglib_cap.move: -------------------------------------------------------------------------------- 1 | // manages the creation of authentication capabilities for msglib 2 | // 1/ msglib_auth root account allows() a new msglib account to create caps (one-time only) 3 | // 2/ the newly allowed msglib account calls register_msglib() at the endpoint, which then forwards the signer to 4 | // here new_version() to retrive the capaibility. 5 | // (a) The send_cap will be stored at the endpoint and 6 | // (b) the receive_cap will be stored at the msglib_receive side for authentication. 7 | // the new version could be either a new MAJOR or MINOR version 8 | module msglib_auth::msglib_cap { 9 | use std::signer::address_of; 10 | use std::error; 11 | use layerzero_common::semver::{Self, SemVer}; 12 | use std::acl::{Self, ACL}; 13 | use layerzero_common::utils::{assert_signer, assert_type_signer}; 14 | 15 | const EMSGLIB_VERSION_NOT_SUPPORTED: u64 = 0x00; 16 | 17 | struct GlobalStore has key { 18 | last_version: SemVer, 19 | msglib_acl: ACL, 20 | } 21 | 22 | struct MsgLibSendCapability has store { 23 | version: SemVer, 24 | } 25 | 26 | struct MsgLibReceiveCapability has store { 27 | version: SemVer, 28 | } 29 | 30 | fun init_module(account: &signer) { 31 | move_to(account, GlobalStore { 32 | last_version: semver::build_version(0, 0), 33 | msglib_acl: acl::empty(), 34 | }) 35 | } 36 | 37 | public fun new_version(account: &signer, major: bool): (SemVer, MsgLibSendCapability, MsgLibReceiveCapability) acquires GlobalStore { 38 | assert_type_signer(account); 39 | 40 | let store = borrow_global_mut(@msglib_auth); 41 | acl::assert_contains(&store.msglib_acl, address_of(account)); 42 | // remove from ACl after registration 43 | acl::remove(&mut store.msglib_acl, address_of(account)); 44 | 45 | let version = if (major) { 46 | semver::next_major(&store.last_version) 47 | } else { 48 | semver::next_minor(&store.last_version) 49 | }; 50 | store.last_version = version; 51 | (version, MsgLibSendCapability { version }, MsgLibReceiveCapability { version }) 52 | } 53 | 54 | public entry fun allow(account: &signer, msglib_receive: address) acquires GlobalStore { 55 | assert_signer(account, @msglib_auth); 56 | 57 | let store = borrow_global_mut(@msglib_auth); 58 | acl::add(&mut store.msglib_acl, msglib_receive); 59 | } 60 | 61 | public entry fun disallow(account: &signer, msglib_receive: address) acquires GlobalStore { 62 | assert_signer(account, @msglib_auth); 63 | 64 | let store = borrow_global_mut(@msglib_auth); 65 | acl::remove(&mut store.msglib_acl, msglib_receive); 66 | } 67 | 68 | public fun send_version(cap: &MsgLibSendCapability): SemVer { 69 | cap.version 70 | } 71 | 72 | public fun receive_version(cap: &MsgLibReceiveCapability): SemVer { 73 | cap.version 74 | } 75 | 76 | public fun assert_send_version(cap: &MsgLibSendCapability, major: u64, minor: u8) { 77 | let (cap_major, cap_minor) = semver::values(&cap.version); 78 | assert!(cap_major == major && cap_minor == minor, error::invalid_argument(EMSGLIB_VERSION_NOT_SUPPORTED)); 79 | } 80 | 81 | public fun assert_receive_version(cap: &MsgLibReceiveCapability, major: u64, minor: u8) { 82 | let (cap_major, cap_minor) = semver::values(&cap.version); 83 | assert!(cap_major == major && cap_minor == minor, error::invalid_argument(EMSGLIB_VERSION_NOT_SUPPORTED)); 84 | } 85 | 86 | #[test_only] 87 | public fun init_module_for_test(account: &signer) { 88 | init_module(account); 89 | } 90 | } -------------------------------------------------------------------------------- /sdk/tasks/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S npx ts-node 2 | import * as commander from "commander" 3 | import { CHAIN_ID } from "@layerzerolabs/core-sdk" 4 | import { wireAll } from "./wireAll" 5 | import { LzConfig, NETWORK_NAME } from "./config" 6 | import { 7 | deployBridge, 8 | deployCommon, 9 | deployCounter, 10 | deployExecutorV2, 11 | deployLayerzero, 12 | deployMsglibV1_1, 13 | deployMsglibV2, 14 | deployOracle, 15 | deployZro, 16 | } from "./deploy" 17 | import * as path from "path" 18 | import { getAccountFromFile, validateStageOfNetworks } from "./utils" 19 | import * as options from "./options" 20 | 21 | const program = new commander.Command() 22 | program.name("aptos-manager").version("0.0.1").description("aptos deploy and config manager") 23 | 24 | program 25 | .command("wireAll") 26 | .description("wire all") 27 | .addOption(options.OPTION_PROMPT) 28 | .addOption(options.OPTION_TO_NETWORKS) 29 | .addOption(options.OPTION_ENV) 30 | .addOption(options.OPTION_STAGE) 31 | .addOption(options.OPTION_KEY_PATH) 32 | .addOption(options.OPTION_FORKED) 33 | .action(async (options) => { 34 | const toNetworks = options.toNetworks 35 | validateStageOfNetworks(options.stage, toNetworks) 36 | const network = NETWORK_NAME[options.stage] 37 | const lookupIds = toNetworks.map((network) => CHAIN_ID[network.replace("-fork", "")]) 38 | const lzConfig = LzConfig(options.stage, lookupIds, options.forked) 39 | 40 | const basePath = path.join(options.keyPath, `stage_${options.stage}`) 41 | const accounts = { 42 | layerzero: getAccountFromFile(path.join(basePath, "layerzero.json")), 43 | relayer: getAccountFromFile(path.join(basePath, "relayer.json")), 44 | executor: getAccountFromFile(path.join(basePath, "executor.json")), 45 | oracle: getAccountFromFile(path.join(basePath, "oracle.json")), 46 | bridge: getAccountFromFile(path.join(basePath, "bridge.json")), 47 | } 48 | 49 | await wireAll(options.stage, options.env, network, toNetworks, options.prompt, accounts, lzConfig) 50 | }) 51 | 52 | const deploy = program 53 | .command("deploy") 54 | .description("deploy") 55 | .addOption(options.OPTION_ENV) 56 | .addOption(options.OPTION_STAGE) 57 | .addOption(options.OPTION_KEY_PATH) 58 | deploy.command("layerzero").action(async (_, command) => { 59 | const options = command.optsWithGlobals() 60 | const endpointId = CHAIN_ID[NETWORK_NAME[options.stage]] 61 | const account = getAccountFromFile(path.join(options.keyPath, `stage_${options.stage}`, "layerzero.json")) 62 | 63 | await deployZro(options.env, account) 64 | await deployCommon(options.env, account) 65 | await deployMsglibV1_1(options.env, account) 66 | await deployMsglibV2(options.env, account) 67 | await deployExecutorV2(options.env, account) 68 | await deployLayerzero(options.env, endpointId, account) 69 | }) 70 | deploy.command("oracle").action(async (_, command) => { 71 | const options = command.optsWithGlobals() 72 | const account = getAccountFromFile(path.join(options.keyPath, `stage_${options.stage}`, "oracle.json")) 73 | await deployOracle(options.env, options.stage, account) 74 | }) 75 | deploy.command("bridge").action(async (_, command) => { 76 | const options = command.optsWithGlobals() 77 | const account = getAccountFromFile(path.join(options.keyPath, `stage_${options.stage}`, "bridge.json")) 78 | await deployBridge(options.env, options.stage, account) 79 | }) 80 | deploy.command("counter").action(async (_, command) => { 81 | const options = command.optsWithGlobals() 82 | const account = getAccountFromFile(path.join(options.keyPath, `stage_${options.stage}`, "counter.json")) 83 | await deployCounter(options.env, options.stage, account) 84 | }) 85 | 86 | program.parse() 87 | -------------------------------------------------------------------------------- /layerzero/sources/msglib-v1/msglib_router.move: -------------------------------------------------------------------------------- 1 | // this module proxies the call to the configured msglib module. 2 | // need to upgrade this module to support new msglib modules 3 | // note: V1 only support uln_send 4 | module layerzero::msglib_router { 5 | use aptos_framework::aptos_coin::AptosCoin; 6 | use aptos_framework::coin::{Coin}; 7 | use layerzero_common::packet::Packet; 8 | use layerzero::msglib_v1_0; 9 | use msglib_v2::msglib_v2_router; 10 | use zro::zro::ZRO; 11 | use msglib_auth::msglib_cap::{Self, MsgLibSendCapability}; 12 | use layerzero_common::semver::{Self, SemVer}; 13 | use msglib_v1_1::msglib_v1_1_router; 14 | 15 | friend layerzero::endpoint; 16 | 17 | // 18 | // interacting with the currently configured version 19 | // 20 | public(friend) fun send( 21 | packet: &Packet, 22 | native_fee: Coin, 23 | zro_fee: Coin, 24 | msglib_params: vector, 25 | cap: &MsgLibSendCapability 26 | ): (Coin, Coin) { 27 | let version = msglib_cap::send_version(cap); 28 | let (major, minor) = semver::values(&version); 29 | 30 | // must also authenticate inside each msglib with send_cap::assert_version(cap); 31 | if (major == 1) { 32 | if (minor == 0) { 33 | msglib_v1_0::send(packet, native_fee, zro_fee, msglib_params, cap) 34 | } else { 35 | msglib_v1_1_router::send(packet, native_fee, zro_fee, msglib_params, cap) 36 | } 37 | } else { 38 | msglib_v2_router::send(packet, native_fee, zro_fee, msglib_params, cap) 39 | } 40 | } 41 | 42 | public(friend) fun set_config( 43 | chain_id: u64, 44 | config_type: u8, 45 | config_bytes: vector, 46 | cap: &MsgLibSendCapability 47 | ) { 48 | let version = msglib_cap::send_version(cap); 49 | let (major, minor) = semver::values(&version); 50 | // must also authenticate inside each msglib with send_cap::assert_version(cap); 51 | if (major == 1) { 52 | if (minor == 0) { 53 | msglib_v1_0::set_ua_config(chain_id, config_type, config_bytes, cap); 54 | } else { 55 | msglib_v1_1_router::set_ua_config(chain_id, config_type, config_bytes, cap); 56 | } 57 | } else { 58 | msglib_v2_router::set_ua_config(chain_id, config_type, config_bytes, cap); 59 | } 60 | } 61 | 62 | // 63 | // public view functions 64 | // 65 | public fun quote(ua_address: address, version: SemVer, dst_chain_id: u64, payload_size: u64, pay_in_zro: bool, msglib_params: vector): (u64, u64) { 66 | let (major, minor) = semver::values(&version); 67 | if (major == 1) { 68 | if (minor == 0) { 69 | msglib_v1_0::quote(ua_address, dst_chain_id, payload_size, pay_in_zro, msglib_params) 70 | } else { 71 | msglib_v1_1_router::quote(ua_address, dst_chain_id, payload_size, pay_in_zro, msglib_params) 72 | } 73 | } else { 74 | msglib_v2_router::quote(ua_address, dst_chain_id, payload_size, pay_in_zro, msglib_params) 75 | } 76 | } 77 | 78 | public fun get_config( 79 | ua_address: address, 80 | version: SemVer, 81 | chain_id: u64, 82 | config_type: u8, 83 | ): vector { 84 | let (major, minor) = semver::values(&version); 85 | if (major == 1) { 86 | if (minor == 0) { 87 | msglib_v1_0::get_ua_config(ua_address, chain_id, config_type) 88 | } else { 89 | msglib_v1_1_router::get_ua_config(ua_address, chain_id, config_type) 90 | } 91 | } else { 92 | msglib_v2_router::get_ua_config(ua_address, chain_id, config_type) 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /sdk/src/modules/endpoint.ts: -------------------------------------------------------------------------------- 1 | import { SDK } from "../index" 2 | import * as aptos from "aptos" 3 | import { TypeInfoEx } from "../types" 4 | import { fullAddress, hexToAscii } from "../utils" 5 | 6 | export class Endpoint { 7 | public readonly module 8 | public readonly moduleName 9 | 10 | constructor(private sdk: SDK) { 11 | this.module = `${sdk.accounts.layerzero}::endpoint` 12 | this.moduleName = "layerzero::endpoint" 13 | } 14 | 15 | async initialize(signer: aptos.AptosAccount, localChainId: aptos.BCS.Uint16): Promise { 16 | const transaction: aptos.Types.EntryFunctionPayload = { 17 | function: `${this.module}::init`, 18 | type_arguments: [], 19 | arguments: [localChainId], 20 | } 21 | 22 | return this.sdk.sendAndConfirmTransaction(signer, transaction) 23 | } 24 | 25 | async getUATypeInfo(uaAddress: aptos.MaybeHexString): Promise { 26 | const resource = await this.sdk.client.getAccountResource( 27 | this.sdk.accounts.layerzero!, 28 | `${this.module}::UaRegistry`, 29 | ) 30 | const { ua_infos } = resource.data as { ua_infos: { handle: string } } 31 | const typesHandle = ua_infos.handle 32 | const typeInfo = await this.sdk.client.getTableItem(typesHandle, { 33 | key_type: "address", 34 | value_type: `0x1::type_info::TypeInfo`, 35 | key: aptos.HexString.ensure(uaAddress).toString(), 36 | }) 37 | 38 | const account_address = fullAddress(typeInfo.account_address).toString() 39 | const module_name = hexToAscii(typeInfo.module_name) 40 | const struct_name = hexToAscii(typeInfo.struct_name) 41 | return { 42 | account_address, 43 | module_name, 44 | struct_name, 45 | type: `${account_address}::${module_name}::${struct_name}`, 46 | } 47 | } 48 | 49 | async getOracleFee(oracleAddr: aptos.MaybeHexString, dstChainId: aptos.BCS.Uint16): Promise { 50 | const resource = await this.sdk.client.getAccountResource( 51 | this.sdk.accounts.layerzero!, 52 | `${this.module}::FeeStore`, 53 | ) 54 | const { oracle_fees } = resource.data as { oracle_fees: { handle: string } } 55 | const response = await this.sdk.client.getTableItem(oracle_fees.handle, { 56 | key_type: `${this.module}::QuoteKey`, 57 | value_type: "u64", 58 | key: { 59 | agent: aptos.HexString.ensure(oracleAddr).toString(), 60 | chain_id: dstChainId.toString(), 61 | }, 62 | }) 63 | return BigInt(response) 64 | } 65 | 66 | async getRegisterEvents(start: bigint, limit: number): Promise { 67 | return this.sdk.client.getEventsByEventHandle( 68 | this.sdk.accounts.layerzero!, 69 | `${this.module}::UaRegistry`, 70 | "register_events", 71 | { start, limit }, 72 | ) 73 | } 74 | 75 | async quoteFee( 76 | uaAddress: aptos.MaybeHexString, 77 | dstChainId: aptos.BCS.Uint16, 78 | adapterParams: aptos.BCS.Bytes, 79 | payloadSize: number, 80 | ): Promise { 81 | let totalFee = BigInt(await this.sdk.LayerzeroModule.Uln.Config.quoteFee(uaAddress, dstChainId, payloadSize)) 82 | 83 | const [executor] = await this.sdk.LayerzeroModule.ExecutorConfig.getExecutor(uaAddress, dstChainId) 84 | 85 | totalFee += await this.sdk.LayerzeroModule.Executor.quoteFee(executor, dstChainId, adapterParams) 86 | 87 | return totalFee 88 | } 89 | 90 | registerExecutorPayload(executorType: string): aptos.Types.EntryFunctionPayload { 91 | return { 92 | function: `${this.module}::register_executor`, 93 | type_arguments: [executorType], 94 | arguments: [], 95 | } 96 | } 97 | 98 | async registerExecutor(signer: aptos.AptosAccount, executorType: string): Promise { 99 | const transaction = this.registerExecutorPayload(executorType) 100 | return await this.sdk.sendAndConfirmTransaction(signer, transaction) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Business Source License 1.1 2 | 3 | License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. 4 | "Business Source License" is a trademark of MariaDB Corporation Ab. 5 | 6 | ----------------------------------------------------------------------------- 7 | 8 | Parameters 9 | 10 | Licensor: LayerZero Labs Ltd 11 | 12 | Licensed Work: LayerZero Protocol 13 | The Licensed Work is (c) 2022 LayerZero Labs Ltd 14 | 15 | Change Date: 2025-10-18 16 | 17 | ----------------------------------------------------------------------------- 18 | 19 | Terms 20 | 21 | The Licensor hereby grants you the right to copy, modify, create derivative 22 | works, redistribute, and make non-production use of the Licensed Work. The 23 | Licensor may make an Additional Use Grant, above, permitting limited 24 | production use. 25 | 26 | Effective on the Change Date, or the fourth anniversary of the first publicly 27 | available distribution of a specific version of the Licensed Work under this 28 | License, whichever comes first, the Licensor hereby grants you rights under 29 | the terms of the Change License, and the rights granted in the paragraph 30 | above terminate. 31 | 32 | If your use of the Licensed Work does not comply with the requirements 33 | currently in effect as described in this License, you must purchase a 34 | commercial license from the Licensor, its affiliated entities, or authorized 35 | resellers, or you must refrain from using the Licensed Work. 36 | 37 | All copies of the original and modified Licensed Work, and derivative works 38 | of the Licensed Work, are subject to this License. This License applies 39 | separately for each version of the Licensed Work and the Change Date may vary 40 | for each version of the Licensed Work released by Licensor. 41 | 42 | You must conspicuously display this License on each original or modified copy 43 | of the Licensed Work. If you receive the Licensed Work in original or 44 | modified form from a third party, the terms and conditions set forth in this 45 | License apply to your use of that work. 46 | 47 | Any use of the Licensed Work in violation of this License will automatically 48 | terminate your rights under this License for the current and all other 49 | versions of the Licensed Work. 50 | 51 | This License does not grant you any right in any trademark or logo of 52 | Licensor or its affiliates (provided that you may use a trademark or logo of 53 | Licensor as expressly required by this License). 54 | 55 | TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON 56 | AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, 57 | EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF 58 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND 59 | TITLE. 60 | 61 | MariaDB hereby grants you permission to use this License’s text to license 62 | your works, and to refer to it using the trademark "Business Source License", 63 | as long as you comply with the Covenants of Licensor below. 64 | 65 | ----------------------------------------------------------------------------- 66 | 67 | Covenants of Licensor 68 | 69 | In consideration of the right to use this License’s text and the "Business 70 | Source License" name and trademark, Licensor covenants to MariaDB, and to all 71 | other recipients of the licensed work to be provided by Licensor: 72 | 73 | 1. To specify as the Change License the GPL Version 2.0 or any later version, 74 | or a license that is compatible with GPL Version 2.0 or a later version, 75 | where "compatible" means that software provided under the Change License can 76 | be included in a program with software provided under GPL Version 2.0 or a 77 | later version. Licensor may specify additional Change Licenses without 78 | limitation. 79 | 80 | 2. To either: (a) specify an additional grant of rights to use that does not 81 | impose any additional restriction on the right granted in this License, as 82 | the Additional Use Grant; or (b) insert the text "None". 83 | 84 | 3. To specify a Change Date. 85 | 86 | 4. Not to modify this License in any other way. 87 | 88 | ----------------------------------------------------------------------------- 89 | 90 | Notice 91 | 92 | The Business Source License (this document, or the "License") is not an Open 93 | Source license. However, the Licensed Work will eventually be made available 94 | under an Open Source License, as stated in this License. -------------------------------------------------------------------------------- /apps/bridge-evm/LICENSE: -------------------------------------------------------------------------------- 1 | Business Source License 1.1 2 | 3 | License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. 4 | "Business Source License" is a trademark of MariaDB Corporation Ab. 5 | 6 | ----------------------------------------------------------------------------- 7 | 8 | Parameters 9 | 10 | Licensor: LayerZero Labs Ltd 11 | 12 | Licensed Work: Stargate Core 13 | The Licensed Work is (c) 2022 LayerZero Labs Ltd 14 | 15 | Change Date: 2026-03-17 16 | 17 | ----------------------------------------------------------------------------- 18 | 19 | Terms 20 | 21 | The Licensor hereby grants you the right to copy, modify, create derivative 22 | works, redistribute, and make non-production use of the Licensed Work. The 23 | Licensor may make an Additional Use Grant, above, permitting limited 24 | production use. 25 | 26 | Effective on the Change Date, or the fourth anniversary of the first publicly 27 | available distribution of a specific version of the Licensed Work under this 28 | License, whichever comes first, the Licensor hereby grants you rights under 29 | the terms of the Change License, and the rights granted in the paragraph 30 | above terminate. 31 | 32 | If your use of the Licensed Work does not comply with the requirements 33 | currently in effect as described in this License, you must purchase a 34 | commercial license from the Licensor, its affiliated entities, or authorized 35 | resellers, or you must refrain from using the Licensed Work. 36 | 37 | All copies of the original and modified Licensed Work, and derivative works 38 | of the Licensed Work, are subject to this License. This License applies 39 | separately for each version of the Licensed Work and the Change Date may vary 40 | for each version of the Licensed Work released by Licensor. 41 | 42 | You must conspicuously display this License on each original or modified copy 43 | of the Licensed Work. If you receive the Licensed Work in original or 44 | modified form from a third party, the terms and conditions set forth in this 45 | License apply to your use of that work. 46 | 47 | Any use of the Licensed Work in violation of this License will automatically 48 | terminate your rights under this License for the current and all other 49 | versions of the Licensed Work. 50 | 51 | This License does not grant you any right in any trademark or logo of 52 | Licensor or its affiliates (provided that you may use a trademark or logo of 53 | Licensor as expressly required by this License). 54 | 55 | TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON 56 | AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, 57 | EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF 58 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND 59 | TITLE. 60 | 61 | MariaDB hereby grants you permission to use this License’s text to license 62 | your works, and to refer to it using the trademark "Business Source License", 63 | as long as you comply with the Covenants of Licensor below. 64 | 65 | ----------------------------------------------------------------------------- 66 | 67 | Covenants of Licensor 68 | 69 | In consideration of the right to use this License’s text and the "Business 70 | Source License" name and trademark, Licensor covenants to MariaDB, and to all 71 | other recipients of the licensed work to be provided by Licensor: 72 | 73 | 1. To specify as the Change License the GPL Version 2.0 or any later version, 74 | or a license that is compatible with GPL Version 2.0 or a later version, 75 | where "compatible" means that software provided under the Change License can 76 | be included in a program with software provided under GPL Version 2.0 or a 77 | later version. Licensor may specify additional Change Licenses without 78 | limitation. 79 | 80 | 2. To either: (a) specify an additional grant of rights to use that does not 81 | impose any additional restriction on the right granted in this License, as 82 | the Additional Use Grant; or (b) insert the text "None". 83 | 84 | 3. To specify a Change Date. 85 | 86 | 4. Not to modify this License in any other way. 87 | 88 | ----------------------------------------------------------------------------- 89 | 90 | Notice 91 | 92 | The Business Source License (this document, or the "License") is not an Open 93 | Source license. However, the Licensed Work will eventually be made available 94 | under an Open Source License, as stated in this License. -------------------------------------------------------------------------------- /layerzero/sources/app/test_helpers.move: -------------------------------------------------------------------------------- 1 | #[test_only] 2 | module layerzero::test_helpers { 3 | use layerzero::uln_receive; 4 | use layerzero::msglib_v1_0; 5 | use layerzero::uln_config; 6 | use layerzero::uln_signer; 7 | use layerzero::channel; 8 | use layerzero::msglib_config; 9 | use layerzero_common::packet::{Self, Packet}; 10 | use std::signer::address_of; 11 | use layerzero::endpoint; 12 | use layerzero::executor_config; 13 | use layerzero::executor_v1::{Self, Executor}; 14 | use layerzero::packet_event; 15 | use layerzero::bulletin; 16 | use layerzero::admin; 17 | use aptos_framework::aptos_account; 18 | use msglib_auth::msglib_cap; 19 | use executor_auth::executor_cap; 20 | 21 | // setup layerzero with 1% treasury fee 22 | // setup a relayer with 100 base fee and 1 fee per bytes 23 | // setup an oracle with 10 base fee and 0 fee per bytes 24 | // setup default app config with above relayer and oracle and 15 block confirmations 25 | // setup a default executor config with 0 gas price 26 | public fun setup_layerzero_for_test( 27 | lz: &signer, 28 | msglib_auth: &signer, 29 | oracle_root: &signer, 30 | relayer_root: &signer, 31 | executor_root: &signer, 32 | executor_auth: &signer, 33 | src_chain_id: u64, 34 | dst_chain_id: u64, 35 | ) { 36 | // init modules first as if we deployed 37 | channel::init_module_for_test(lz); 38 | 39 | msglib_config::init_module_for_test(lz); 40 | 41 | // msgliv v1 42 | admin::init_module_for_test(lz); 43 | uln_config::init_module_for_test(lz); 44 | msglib_v1_0::init_module_for_test(lz); 45 | packet_event::init_module_for_test(lz); 46 | executor_v1::init_module_for_test(lz); 47 | 48 | // init endpoint 49 | bulletin::init_module_for_test(lz); 50 | 51 | // msg auth 52 | msglib_cap::init_module_for_test(msglib_auth); 53 | msglib_cap::allow(msglib_auth, @layerzero); 54 | // executor 55 | executor_config::init_module_for_test(lz); 56 | executor_cap::init_module_for_test(executor_auth); 57 | 58 | endpoint::init(lz, src_chain_id); 59 | uln_receive::init(lz); 60 | msglib_config::set_default_send_msglib(lz, dst_chain_id, 1, 0); 61 | msglib_config::set_default_receive_msglib(lz, dst_chain_id, 1, 0); 62 | 63 | endpoint::register_executor(executor_auth); 64 | executor_config::set_default_executor(lz, dst_chain_id, 1, address_of(executor_root)); 65 | executor_v1::set_default_adapter_params(lz, dst_chain_id, vector[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]); 66 | executor_v1::register(executor_root); 67 | executor_v1::set_fee(executor_root, dst_chain_id, 100000, 0, 0); 68 | 69 | uln_config::set_chain_address_size(lz, dst_chain_id,32); 70 | msglib_v1_0::set_treasury_fee(lz, 100); // 1% 71 | // register offchain workers 72 | uln_signer::register(oracle_root); 73 | uln_signer::register(relayer_root); 74 | uln_signer::set_fee(oracle_root, dst_chain_id, 10, 0); 75 | uln_signer::set_fee(relayer_root, dst_chain_id, 100, 1); 76 | 77 | let oracle_addr = address_of(oracle_root); 78 | let relayer_addr = address_of(relayer_root); 79 | uln_config::set_default_config(lz, dst_chain_id, oracle_addr, relayer_addr, 15, 15); 80 | } 81 | 82 | public fun deliver_packet(oracle_root: &signer, relayer_root: &signer, emitted_packet: Packet, confirmation: u64) { 83 | let hash = packet::hash_sha3_packet(&emitted_packet); 84 | uln_receive::oracle_propose(oracle_root, hash, confirmation); 85 | uln_receive::relayer_verify( 86 | relayer_root, 87 | packet::encode_packet(&emitted_packet), 88 | confirmation, 89 | ); 90 | } 91 | 92 | public fun setup_layerzero_for_oracle_test( 93 | lz: &signer, 94 | msglib_auth: &signer, 95 | ) { 96 | aptos_account::create_account(address_of(lz)); 97 | 98 | // init modules first as if we deployed 99 | msglib_config::init_module_for_test(lz); 100 | bulletin::init_module_for_test(lz); 101 | // msgliv v1 102 | uln_config::init_module_for_test(lz); 103 | msglib_v1_0::init_module_for_test(lz); 104 | 105 | msglib_cap::init_module_for_test(msglib_auth); 106 | msglib_cap::allow(msglib_auth, @layerzero); 107 | 108 | // init endpoint 109 | endpoint::init(lz, 108); 110 | 111 | // init and register msglib 112 | uln_receive::init(lz); 113 | } 114 | } -------------------------------------------------------------------------------- /sdk/src/modules/channel.ts: -------------------------------------------------------------------------------- 1 | import { SDK } from "../index" 2 | import * as aptos from "aptos" 3 | import { isErrorOfApiError } from "../utils" 4 | 5 | export interface ChannelType { 6 | outbound_nonce: string 7 | inbound_nonce: string 8 | payload_hashs: { handle: string } 9 | } 10 | 11 | export class Channel { 12 | public readonly module 13 | 14 | constructor(private sdk: SDK) { 15 | this.module = `${sdk.accounts.layerzero}::channel` 16 | } 17 | 18 | async getOutboundEvents(start: bigint, limit: number): Promise { 19 | return this.sdk.client.getEventsByEventHandle( 20 | this.sdk.accounts.layerzero!, 21 | `${this.module}::EventStore`, 22 | "outbound_events", 23 | { start, limit }, 24 | ) 25 | } 26 | 27 | async getInboundEvents(start: bigint, limit: number): Promise { 28 | return this.sdk.client.getEventsByEventHandle( 29 | this.sdk.accounts.layerzero!, 30 | `${this.module}::EventStore`, 31 | "inbound_events", 32 | { start, limit }, 33 | ) 34 | } 35 | 36 | async getReceiveEvents(start: bigint, limit: number): Promise { 37 | return this.sdk.client.getEventsByEventHandle( 38 | this.sdk.accounts.layerzero!, 39 | `${this.module}::EventStore`, 40 | "receive_events", 41 | { 42 | start, 43 | limit, 44 | }, 45 | ) 46 | } 47 | 48 | async getChannelState( 49 | uaAddress: aptos.MaybeHexString, 50 | remoteChainId: aptos.BCS.Uint16, 51 | remoteAddress: aptos.BCS.Bytes, 52 | ): Promise { 53 | const resource = await this.sdk.client.getAccountResource(uaAddress, `${this.module}::Channels`) 54 | const { states } = resource.data as { states: { handle: string } } 55 | const pathsHandle = states.handle 56 | 57 | return this.sdk.client.getTableItem(pathsHandle, { 58 | key_type: `${this.module}::Remote`, 59 | value_type: `${this.module}::Channel`, 60 | key: { chain_id: remoteChainId.toString(), addr: Buffer.from(remoteAddress).toString("hex") }, 61 | }) 62 | } 63 | 64 | async getOutboundNonce( 65 | uaAddress: aptos.MaybeHexString, 66 | remoteChainId: aptos.BCS.Uint16, 67 | remoteAddress: aptos.BCS.Bytes, 68 | ): Promise { 69 | try { 70 | const pathInfo = await this.getChannelState(uaAddress, remoteChainId, remoteAddress) 71 | const outboundNonce = pathInfo.outbound_nonce 72 | return BigInt(outboundNonce) 73 | } catch (e) { 74 | if (isErrorOfApiError(e, 404)) { 75 | return BigInt(0) 76 | } 77 | throw e 78 | } 79 | } 80 | 81 | async getInboundNonce( 82 | uaAddress: aptos.MaybeHexString, 83 | remoteChainId: aptos.BCS.Uint16, 84 | remoteAddress: aptos.BCS.Bytes, 85 | ): Promise { 86 | try { 87 | const pathInfo = await this.getChannelState(uaAddress, remoteChainId, remoteAddress) 88 | const inboundNonce = pathInfo.inbound_nonce 89 | return BigInt(inboundNonce) 90 | } catch (e) { 91 | if (isErrorOfApiError(e, 404)) { 92 | return BigInt(0) 93 | } 94 | throw e 95 | } 96 | } 97 | 98 | async getPayloadHash( 99 | uaAddress: aptos.MaybeHexString, 100 | remoteChainId: aptos.BCS.Uint16, 101 | remoteAddress: aptos.BCS.Bytes, 102 | nonce: aptos.BCS.Uint64, 103 | ): Promise { 104 | try { 105 | const pathInfo = await this.getChannelState(uaAddress, remoteChainId, remoteAddress) 106 | const resource = pathInfo.payload_hashs 107 | 108 | return await this.sdk.client.getTableItem(resource.handle, { 109 | key_type: "u64", 110 | value_type: "vector", 111 | key: nonce.toString(), 112 | }) 113 | } catch (e) { 114 | if (isErrorOfApiError(e, 404)) { 115 | return "" 116 | } 117 | throw e 118 | } 119 | } 120 | 121 | async isProofDelivered( 122 | uaAddress: aptos.MaybeHexString, 123 | remoteChainId: aptos.BCS.Uint16, 124 | remoteAddress: aptos.BCS.Bytes, 125 | nonce: aptos.BCS.Uint64, 126 | ): Promise { 127 | const inboundNonce = await this.getInboundNonce(uaAddress, remoteChainId, remoteAddress) 128 | console.log(`inboundNonce: ${inboundNonce}`) 129 | console.log(`nonce: ${nonce}`) 130 | if (nonce <= inboundNonce) { 131 | return true 132 | } 133 | const payloadHash = await this.getPayloadHash(uaAddress, remoteChainId, remoteAddress, nonce) 134 | return payloadHash !== "" 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /apps/bridge-evm/tasks/send.ts: -------------------------------------------------------------------------------- 1 | import * as crossChainHelper from "./utils/crossChainHelper" 2 | import { ethers } from "ethers" 3 | import { TOKEN_ADDRESSES } from "./config/addresses" 4 | import { CHAIN_ID } from "@layerzerolabs/core-sdk" 5 | import { CoinType } from "../../../sdk/src/modules/apps/coin" 6 | 7 | const abiDecoder = require("abi-decoder") 8 | 9 | const ABIs = [ 10 | "../deployments/goerli-sandbox/TokenBridge.json", 11 | "../artifacts/@layerzerolabs/solidity-examples/contracts/lzApp/NonblockingLzApp.sol/NonblockingLzApp.json", 12 | "@layerzerolabs/layerzero-core/artifacts/contracts/UltraLightNodeV2.sol/UltraLightNodeV2.json", 13 | "@layerzerolabs/layerzero-core/artifacts/contracts/RelayerV2.sol/RelayerV2.json", 14 | "@layerzerolabs/layerzero-core/artifacts/contracts/Endpoint.sol/Endpoint.json", 15 | "@layerzerolabs/d2-contracts/artifacts/contracts/OracleV2.sol/OracleV2.json", 16 | ] 17 | 18 | try { 19 | for (const abi of ABIs) { 20 | abiDecoder.addABI(require(abi).abi) 21 | } 22 | } catch (e) {} 23 | 24 | // npx hardhat --network ethereum-fork send 25 | module.exports = async function (taskArgs, hre) { 26 | // await inspect(hre.network.name) 27 | // return 28 | 29 | const network = hre.network.name 30 | const lookupChainId = CHAIN_ID[network.replace("-fork", "")] 31 | 32 | crossChainHelper.setForking(network.includes("fork"), ["TokenBridge"]) 33 | console.log(`Forking: ${crossChainHelper.FORKING}`) 34 | 35 | const signers = await hre.ethers.getSigners() 36 | const signer = signers[0] 37 | 38 | const amount = ethers.BigNumber.from(taskArgs.a) 39 | console.log(`amount: ${amount}`) 40 | 41 | const bridge = await crossChainHelper.getContract(hre, network, "TokenBridge") 42 | // const aptosChainId = await bridge.aptosChainId() 43 | // console.log(`aptosChainId: ${aptosChainId}`) 44 | 45 | const receiverAddr = taskArgs.r 46 | const lzCallParams = [signer.address, ethers.constants.AddressZero] 47 | const adapterParams = ethers.utils.solidityPack(["uint16", "uint256", "uint256", "bytes"], [2, 5000, 1000000, receiverAddr]) 48 | const { nativeFee, zroFee } = await bridge.quoteForSend(lzCallParams, adapterParams) 49 | 50 | const args = [receiverAddr, amount.toString(), lzCallParams, adapterParams] 51 | 52 | let receipt 53 | const tokenType = taskArgs.t 54 | switch (tokenType) { 55 | case "ETH": { 56 | receipt = await crossChainHelper.executeTransaction( 57 | hre, 58 | network, 59 | { 60 | contractName: "TokenBridge", 61 | methodName: "sendETHToAptos", 62 | args, 63 | txArgs: { value: amount.add(nativeFee) }, 64 | }, 65 | true 66 | ) 67 | break 68 | } 69 | case CoinType.USDT: 70 | case CoinType.WETH: 71 | case CoinType.USDC: { 72 | const tokenAddress = TOKEN_ADDRESSES[tokenType][lookupChainId] 73 | const provider = crossChainHelper.getProvider(network) 74 | const token = new ethers.Contract( 75 | tokenAddress, 76 | new ethers.utils.Interface(["function approve(address,uint) public returns (bool)"]) 77 | ).connect(provider) 78 | await (await token.approve(bridge.address, amount)).wait() 79 | 80 | receipt = await crossChainHelper.executeTransaction( 81 | hre, 82 | network, 83 | { 84 | contractName: "TokenBridge", 85 | methodName: "sendToAptos", 86 | args: [tokenAddress, receiverAddr, amount, lzCallParams, adapterParams], 87 | txArgs: { value: amount.add(nativeFee) }, 88 | }, 89 | false 90 | ) 91 | break 92 | } 93 | } 94 | 95 | let decodedLogs = abiDecoder.decodeLogs(receipt.logs) 96 | const logs = decodedLogs.map((log) => { 97 | return { 98 | name: log.name, 99 | args: log.events.map((event) => { 100 | return `${event.name}(${event.value})` 101 | }), 102 | address: log.address, 103 | } 104 | }) 105 | console.log(logs) 106 | } 107 | 108 | async function inspect(network: string) { 109 | const provider = crossChainHelper.getProvider(network) 110 | const receipt = await provider.getTransactionReceipt("0x9cc64ee01e28a81f5a02bf7e4713aec058b796aac55a8c900d0c60c6933d1c9a") 111 | console.log(receipt.blockNumber) 112 | const decodedLogs = abiDecoder.decodeLogs(receipt.logs) 113 | const logs = decodedLogs.map((log) => { 114 | return { 115 | name: log.name, 116 | args: log.events.map((event) => { 117 | return `${event.name}(${event.value})` 118 | }), 119 | address: log.address, 120 | } 121 | }) 122 | console.log(logs) 123 | return 124 | } 125 | -------------------------------------------------------------------------------- /sdk/src/modules/apps/counter.ts: -------------------------------------------------------------------------------- 1 | import * as aptos from "aptos" 2 | import { SDK } from "../../index" 3 | import { LzApp } from "./lzapp" 4 | import { isErrorOfApiError } from "../../utils" 5 | import { UlnConfigType } from "../../types" 6 | 7 | export class Counter { 8 | readonly address: aptos.MaybeHexString 9 | SEND_PAYLOAD_LENGTH: number = 4 10 | private sdk: SDK 11 | private lzApp: LzApp 12 | private readonly uaType: string 13 | 14 | constructor(sdk: SDK, counter: aptos.MaybeHexString, lzApp?: aptos.MaybeHexString) { 15 | this.sdk = sdk 16 | this.address = counter 17 | this.lzApp = new LzApp(sdk, lzApp || sdk.accounts.layerzero!, counter) 18 | this.uaType = `${this.address}::counter::CounterUA` 19 | } 20 | 21 | async initialize(signer: aptos.AptosAccount): Promise { 22 | const transaction: aptos.Types.EntryFunctionPayload = { 23 | function: `${this.address}::counter::init`, 24 | type_arguments: [], 25 | arguments: [], 26 | } 27 | 28 | return this.sdk.sendAndConfirmTransaction(signer, transaction) 29 | } 30 | 31 | async getRemote(remoteChainId: aptos.BCS.Uint16): Promise { 32 | return this.lzApp.getRemote(remoteChainId) 33 | } 34 | 35 | async getCount(): Promise { 36 | const resource = await this.sdk.client.getAccountResource(this.address, `${this.address}::counter::Counter`) 37 | const { i } = resource.data as { i: string } 38 | return BigInt(i) 39 | } 40 | 41 | async createCounter( 42 | signer: aptos.AptosAccount, 43 | i: aptos.BCS.Uint64 | aptos.BCS.Uint32, 44 | ): Promise { 45 | const transaction: aptos.Types.EntryFunctionPayload = { 46 | function: `${this.address}::counter::create_counter`, 47 | type_arguments: [], 48 | arguments: [i], 49 | } 50 | 51 | return this.sdk.sendAndConfirmTransaction(signer, transaction) 52 | } 53 | 54 | async setRemote( 55 | signer: aptos.AptosAccount, 56 | remoteChainId: aptos.BCS.Uint16, 57 | remoteAddress: aptos.BCS.Bytes, 58 | ): Promise { 59 | return this.lzApp.setRemote(signer, remoteChainId, remoteAddress) 60 | } 61 | 62 | async setAppConfig( 63 | signer: aptos.AptosAccount, 64 | majorVersion: aptos.BCS.Uint16, 65 | minorVersion: aptos.BCS.Uint8, 66 | remoteChainId: aptos.BCS.Uint16, 67 | configType: aptos.BCS.Uint8, 68 | configBytes: aptos.BCS.Bytes, 69 | ): Promise { 70 | console.log(`configType: ${configType}, configBytes: ${configBytes}`) 71 | return this.lzApp.setConfig( 72 | signer, 73 | this.uaType, 74 | majorVersion, 75 | minorVersion, 76 | remoteChainId, 77 | configType, 78 | configBytes, 79 | ) 80 | } 81 | 82 | async setAppConfigBundle( 83 | signer: aptos.AptosAccount, 84 | majorVersion: aptos.BCS.Uint16, 85 | minorVersion: aptos.BCS.Uint8, 86 | remoteChainId: aptos.BCS.Uint16, 87 | config: UlnConfigType, 88 | ) { 89 | await this.lzApp.setConfigBundle(signer, this.uaType, majorVersion, minorVersion, remoteChainId, config) 90 | } 91 | 92 | async sendToRemote( 93 | signer: aptos.AptosAccount, 94 | remoteChainId: aptos.BCS.Uint16, 95 | fee: aptos.BCS.Uint64 | aptos.BCS.Uint32, 96 | adapterParams: Uint8Array, 97 | ): Promise { 98 | const transaction: aptos.Types.EntryFunctionPayload = { 99 | function: `${this.address}::counter::send_to_remote`, 100 | type_arguments: [], 101 | arguments: [remoteChainId, fee, Array.from(adapterParams)], 102 | } 103 | 104 | return this.sdk.sendAndConfirmTransaction(signer, transaction) 105 | } 106 | 107 | async lzReceive( 108 | signer: aptos.AptosAccount, 109 | remoteChainId: aptos.BCS.Uint16, 110 | remoteAddress: aptos.BCS.Bytes, 111 | payload: aptos.BCS.Bytes, 112 | ): Promise { 113 | const transaction: aptos.Types.EntryFunctionPayload = { 114 | function: `${this.address}::counter::lz_receive`, 115 | type_arguments: [], 116 | arguments: [remoteChainId, Array.from(remoteAddress), Array.from(payload)], 117 | } 118 | 119 | return this.sdk.sendAndConfirmTransaction(signer, transaction) 120 | } 121 | 122 | async isCounterCreated(address: aptos.MaybeHexString): Promise { 123 | try { 124 | const owner = aptos.HexString.ensure(address).toString() 125 | await this.sdk.client.getAccountResource(this.address, `${owner}::counter::Counter`) 126 | return true 127 | } catch (e) { 128 | if (isErrorOfApiError(e, 404)) { 129 | return false 130 | } 131 | throw e 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /layerzero/sources/bulletin.move: -------------------------------------------------------------------------------- 1 | /// A key-value store for UA and msglib to write and read any arbitrary data: 2 | /// 1) UA composability: UA can write data to the store, and other UA can read it. 3 | /// 2) New data types for msglib: new msglibs in the future can write data to the store for UA to read. 4 | module layerzero::bulletin { 5 | use aptos_std::table::{Self, Table}; 6 | use std::error; 7 | use layerzero_common::semver::SemVer; 8 | use layerzero_common::utils::assert_type_signer; 9 | 10 | friend layerzero::endpoint; 11 | 12 | const ELAYERZERO_BULLETIN_EXISTED: u64 = 0x00; 13 | const ELAYERZERO_BULLETIN_NOT_EXISTED: u64 = 0x01; 14 | 15 | struct Bulletin has key, store { 16 | // bytes -> bytes 17 | values: Table, vector>, 18 | } 19 | 20 | struct MsgLibBulletin has key { 21 | // msglib version -> bulletin 22 | bulletin: Table 23 | } 24 | 25 | fun init_module(account: &signer) { 26 | move_to(account, MsgLibBulletin { 27 | bulletin: table::new() 28 | }) 29 | } 30 | 31 | public(friend) fun init_ua_bulletin(account: &signer) { 32 | assert_type_signer(account); 33 | move_to(account, Bulletin { 34 | values: table::new() 35 | }) 36 | } 37 | 38 | public(friend) fun init_msglib_bulletin(version: SemVer) acquires MsgLibBulletin { 39 | let msglib_bulletin = borrow_global_mut(@layerzero); 40 | assert!( 41 | !table::contains(&msglib_bulletin.bulletin, version), 42 | error::already_exists(ELAYERZERO_BULLETIN_EXISTED) 43 | ); 44 | 45 | table::add(&mut msglib_bulletin.bulletin, version, Bulletin { 46 | values: table::new() 47 | }) 48 | } 49 | 50 | public(friend) fun ua_write(ua_address: address, key: vector, value: vector) acquires Bulletin { 51 | let bulletin = borrow_global_mut(ua_address); 52 | table::upsert(&mut bulletin.values, key, value) 53 | } 54 | 55 | public(friend) fun msglib_write(msglib_version: SemVer, key: vector, value: vector) acquires MsgLibBulletin { 56 | let msglib_bulletin = borrow_global_mut(@layerzero); 57 | assert_msglib_bulletin_existed(msglib_bulletin, msglib_version); 58 | 59 | let bulletin = table::borrow_mut(&mut msglib_bulletin.bulletin, msglib_version); 60 | table::upsert(&mut bulletin.values, key, value) 61 | } 62 | 63 | public fun ua_read(ua_address: address, key: vector): vector acquires Bulletin { 64 | let bulletin = borrow_global(ua_address); 65 | *table::borrow(&bulletin.values, key) 66 | } 67 | 68 | public fun msglib_read(msglib_version: SemVer, key: vector): vector acquires MsgLibBulletin { 69 | let msglib_bulletin = borrow_global(@layerzero); 70 | assert_msglib_bulletin_existed(msglib_bulletin, msglib_version); 71 | 72 | let bulletin = table::borrow(&msglib_bulletin.bulletin, msglib_version); 73 | *table::borrow(&bulletin.values, key) 74 | } 75 | 76 | fun assert_msglib_bulletin_existed(msglib_bulletin: &MsgLibBulletin, msglib_version: SemVer) { 77 | assert!( 78 | table::contains(&msglib_bulletin.bulletin, msglib_version), 79 | error::not_found(ELAYERZERO_BULLETIN_NOT_EXISTED) 80 | ); 81 | } 82 | 83 | #[test_only] 84 | use std::signer::address_of; 85 | 86 | #[test_only] 87 | public fun init_module_for_test(account: &signer) { 88 | init_module(account); 89 | } 90 | 91 | #[test_only] 92 | use test::bulletin_test::TestUa; 93 | 94 | #[test_only] 95 | use layerzero_common::semver::build_version; 96 | 97 | #[test_only] 98 | fun setup(lz: &signer, ua: &signer) acquires MsgLibBulletin { 99 | use aptos_framework::aptos_account; 100 | 101 | aptos_account::create_account(address_of(lz)); 102 | init_module_for_test(lz); 103 | 104 | init_msglib_bulletin(build_version(1, 0)); 105 | init_msglib_bulletin(build_version(2, 0)); 106 | init_ua_bulletin(ua); 107 | } 108 | 109 | #[test(lz = @layerzero, ua = @test)] 110 | fun test_set_ua_bulletin(lz: &signer, ua: &signer) acquires MsgLibBulletin, Bulletin { 111 | setup(lz, ua); 112 | 113 | ua_write(address_of(ua), b"key", b"value"); 114 | assert!(ua_read(address_of(ua), b"key") == b"value", 0); 115 | 116 | ua_write(address_of(ua), b"key", b"value2"); 117 | assert!(ua_read(address_of(ua), b"key") == b"value2", 0); 118 | } 119 | 120 | #[test(lz = @layerzero, ua = @test)] 121 | fun test_set_msglib_bulletin(lz: &signer, ua: &signer) acquires MsgLibBulletin { 122 | setup(lz, ua); 123 | 124 | msglib_write(build_version(1, 0), b"key", b"value"); 125 | assert!(msglib_read(build_version(1, 0), b"key") == b"value", 0); 126 | 127 | msglib_write(build_version(2, 0), b"key", b"value2"); 128 | assert!(msglib_read(build_version(2, 0), b"key") == b"value2", 0); 129 | 130 | msglib_write(build_version(1, 0), b"key", b"value3"); 131 | assert!(msglib_read(build_version(1, 0), b"key") == b"value3", 0); 132 | } 133 | } 134 | 135 | #[test_only] 136 | module test::bulletin_test { 137 | struct TestUa {} 138 | } 139 | -------------------------------------------------------------------------------- /layerzero-common/sources/acl.move: -------------------------------------------------------------------------------- 1 | /// ACL has both an allow list and a deny list 2 | /// 1) If one address is in the deny list, it is denied 3 | /// 2) If the allow list is empty and not in the deny list, it is allowed 4 | /// 3) If one address is in the allow list and not in the deny list, it is allowed 5 | /// 4) If the allow list is not empty and the address is not in the allow list, it is denied 6 | module layerzero_common::acl { 7 | use std::vector; 8 | use std::error; 9 | 10 | const ELAYERZERO_ACCESS_DENIED: u64 = 0; 11 | 12 | struct ACL has store, drop { 13 | allow_list: vector
, 14 | deny_list: vector
, 15 | } 16 | 17 | public fun empty(): ACL { 18 | ACL { 19 | allow_list: vector::empty
(), 20 | deny_list: vector::empty
(), 21 | } 22 | } 23 | 24 | /// if not in the allow list, add it. Otherwise, remove it. 25 | public fun allowlist(acl: &mut ACL, addr: address) { 26 | let (found, index) = vector::index_of(&acl.allow_list, &addr); 27 | if (found) { 28 | vector::swap_remove(&mut acl.allow_list, index); 29 | } else { 30 | vector::push_back(&mut acl.allow_list, addr); 31 | }; 32 | } 33 | 34 | /// if not in the deny list, add it. Otherwise, remove it. 35 | public fun denylist(acl: &mut ACL, addr: address) { 36 | let (found, index) = vector::index_of(&acl.deny_list, &addr); 37 | if (found) { 38 | vector::swap_remove(&mut acl.deny_list, index); 39 | } else { 40 | vector::push_back(&mut acl.deny_list, addr); 41 | }; 42 | } 43 | 44 | public fun allowlist_contains(acl: &ACL, addr: &address): bool { 45 | vector::contains(&acl.allow_list, addr) 46 | } 47 | 48 | public fun denylist_contains(acl: &ACL, addr: &address): bool { 49 | vector::contains(&acl.deny_list, addr) 50 | } 51 | 52 | public fun is_allowed(acl: &ACL, addr: &address): bool { 53 | if (vector::contains(&acl.deny_list, addr)) { 54 | return false 55 | }; 56 | 57 | vector::length(&acl.allow_list) == 0 58 | || vector::contains(&acl.allow_list, addr) 59 | } 60 | 61 | public fun assert_allowed(acl: &ACL, addr:& address) { 62 | assert!(is_allowed(acl, addr), error::permission_denied(ELAYERZERO_ACCESS_DENIED)); 63 | } 64 | 65 | #[test] 66 | fun test_allowlist() { 67 | let alice = @1122; 68 | let bob = @3344; 69 | let carol = @5566; 70 | let acl = empty(); 71 | 72 | // add alice and bob to the allow list 73 | allowlist(&mut acl, alice); 74 | allowlist(&mut acl, bob); 75 | assert!(allowlist_contains(&acl, &alice), 0); 76 | assert!(allowlist_contains(&acl, &bob), 0); 77 | assert!(!allowlist_contains(&acl, &carol), 0); 78 | 79 | // remove alice from the allow list 80 | allowlist(&mut acl, alice); 81 | assert!(!allowlist_contains(&acl, &alice), 0); 82 | assert!(allowlist_contains(&acl, &bob), 0); 83 | assert!(!allowlist_contains(&acl, &carol), 0); 84 | } 85 | 86 | #[test] 87 | fun test_denylist() { 88 | let alice = @1122; 89 | let bob = @3344; 90 | let carol = @5566; 91 | let acl = empty(); 92 | 93 | // add alice and bob to the deny list 94 | denylist(&mut acl, alice); 95 | denylist(&mut acl, bob); 96 | assert!(denylist_contains(&acl, &alice), 0); 97 | assert!(denylist_contains(&acl, &bob), 0); 98 | assert!(!denylist_contains(&acl, &carol), 0); 99 | 100 | // remove alice from the deny list 101 | denylist(&mut acl, alice); 102 | assert!(!denylist_contains(&acl, &alice), 0); 103 | assert!(denylist_contains(&acl, &bob), 0); 104 | assert!(!denylist_contains(&acl, &carol), 0); 105 | } 106 | 107 | #[test] 108 | fun test_assert_allowed() { 109 | let alice = @1122; 110 | let bob = @3344; 111 | let carol = @5566; 112 | let acl = empty(); 113 | 114 | // add carol to the deny list, then assert that alice and bob are allowed 115 | denylist(&mut acl, carol); 116 | assert_allowed(&acl, &alice); 117 | assert_allowed(&acl, &bob); 118 | assert!(!is_allowed(&acl, &carol), 0); 119 | 120 | // add alice to the allow list, then assert that alice is allowed and bob is not 121 | allowlist(&mut acl, alice); 122 | assert_allowed(&acl, &alice); 123 | assert!(!is_allowed(&acl, &bob), 0); 124 | 125 | // add bob to the allow list, then assert that alice and bob are allowed 126 | allowlist(&mut acl, bob); 127 | assert_allowed(&acl, &alice); 128 | assert_allowed(&acl, &bob); 129 | 130 | // add bob to the deny list, then assert that bob is not allowed even though he is in the allow list 131 | denylist(&mut acl, bob); 132 | assert_allowed(&acl, &alice); 133 | assert!(!is_allowed(&acl, &bob), 0); 134 | assert!(!is_allowed(&acl, &carol), 0); 135 | 136 | // remove all from lists, then assert that all are allowed 137 | allowlist(&mut acl, alice); 138 | allowlist(&mut acl, bob); 139 | denylist(&mut acl, bob); 140 | denylist(&mut acl, carol); 141 | assert_allowed(&acl, &alice); 142 | assert_allowed(&acl, &bob); 143 | assert_allowed(&acl, &carol); 144 | } 145 | } -------------------------------------------------------------------------------- /apps/README.md: -------------------------------------------------------------------------------- 1 | # Building on LayerZero 2 | 3 | It is simple to build your own applications (aka `UA`) on LayerZero. You just need to integrate your UA with three interfaces of Endpoint: 4 | 5 | - register_ua() 6 | - send() 7 | - lz_receive() 8 | 9 | ## Register UA 10 | Before sending messages on LayerZero, you need to register your UA. 11 | 12 | ```move 13 | public fun register_ua(account: &signer): UaCapability 14 | ``` 15 | 16 | The `UA` type is an identifier of your application. You can use any type as `UA`, e.g. `0x1::MyApp::MyApp` as UA. 17 | Note: only one UA is allowed per address. That means there won't two `UA` types share the same address. 18 | 19 | When calling `register_ua()`, you will get a `UaCapability` as return. It is the resources for authenticating any LayerZero functions, such as sending messages and setting configurations. 20 | 21 | ## Send Messages 22 | 23 | To send a message, call the Endpoint's `send()` function. 24 | 25 | ```move 26 | public fun send( 27 | dst_chain_id: u64, 28 | dst_address: vector, 29 | payload: vector, 30 | native_fee: Coin, 31 | zro_fee: Coin, 32 | adapter_params: vector, 33 | msglib_params: vector, 34 | _cap: &UaCapability 35 | ): (u64, Coin, Coin) 36 | ``` 37 | 38 | You can send any message (`payload`) to any address on any chain and pay fee with `AptosCoin`. So far we only support `AptosCoin` as fee. 39 | `ZRO` coin will be supported to pay the protocol fee in the future. 40 | 41 | The `msglib_params` is for passing parameters to the message libraries. So far, it is not used and can be empty. 42 | 43 | ### Estimate Fee 44 | 45 | If you want to know how much `AptosCoin` to pay for the message, you can call the Endpoint's `quote_fee()` to get the fee tuple (native_fee (in coin), layerzero_fee (in coin)). 46 | 47 | ```move 48 | #[view] 49 | public fun quote_fee( 50 | ua_address: address, 51 | dst_chain_id: u64, 52 | payload_size: u64, 53 | pay_in_zro: bool, 54 | adapter_params: vector, 55 | msglib_params: vector 56 | ): (u64, u64) 57 | ``` 58 | 59 | 60 | ## Receive Messages 61 | 62 | Your UA has to provide a public entry function `lz_receive()` for executors to receive messages from other chains and execute your business logic. 63 | 64 | ```move 65 | public entry fun lz_receive(src_chain_id: u64, src_address: vector, payload: vector) 66 | ``` 67 | 68 | The `lz_receive()` function has to call the Endpoint's `lz_receive()` function to verify the payload and get the nonce. 69 | 70 | ```move 71 | // endpoint's lz_receive() 72 | public fun lz_receive( 73 | src_chain_id: u64, 74 | src_address: vector, 75 | payload: vector, 76 | _cap: &UaCapability 77 | ): u64 78 | ``` 79 | 80 | When an executor calls your UA's `lz_receive()`, it needs to know what generic types `` to use for consuming the payload. 81 | So if your UA needs those types, you also need to provide a public entry function `lz_receive_types()` to return the types. 82 | 83 | NOTES: make sure to assert the provided types against the payload. For example, if the payload indicates coinType A, then the provided coinType must be A. 84 | 85 | ```move 86 | #[view] 87 | public fun lz_receive_types(src_chain_id: u64, src_address: vector, payload: vector): vector 88 | ``` 89 | 90 | ### Blocking Mode 91 | 92 | Layerzero is by default BLOCKING, which means if the message payload fails in the lz_receive function, 93 | your UA will be blocked and cannot receive next messages from that path until the failed message is received successfully. 94 | For that case, you may have to drop the message or store it and retry later. We provide [LzApp Modules](#LzApp-Modules) to help you handle it. 95 | 96 | 97 | ## UA Custom Config 98 | 99 | You can also customize your UA's configurations, e.g. message library, relayer and oracle, etc. 100 | 101 | ```move 102 | public fun set_config( 103 | major_version: u64, 104 | minor_version: u8, 105 | chain_id: u64, 106 | config_type: u8, 107 | config_bytes: vector, 108 | _cap: &UaCapability 109 | ) 110 | 111 | public fun set_send_msglib(chain_id: u64, major_version: u64, minor_version: u8, _cap: &UaCapability) 112 | 113 | public fun set_receive_msglib(chain_id: u64, major_version: u64, minor_version: u8, _cap: &UaCapability) 114 | 115 | public fun set_executor(chain_id: u64, version: u64, executor: address, _cap: &UaCapability) 116 | ``` 117 | 118 | ## LzApp Modules 119 | 120 | We provide some common modules to help build your UAs to let you put more focus on your business logic. 121 | Those modules provide many useful functions that are commonly used in most UAs. You can just use them directly 122 | that are already deployed by LayerZero, or you can copy them to your own modules and modify them to fit your needs. 123 | 124 | - [lzapp.move](../layerzero/sources/app/lzapp/lzapp.move) 125 | - [remote.move](../layerzero/sources/app/lzapp/remote.move) 126 | 127 | ### LzApp 128 | 129 | LZApp module provides a simple way for you to manage your UA's configurations and handle error messages. 130 | 1. provides entry functions to config instead of calling from app with UaCapability 131 | 2. allows the app to drop/store the next payload 132 | 3. enables to send lz message with both Aptos coin and with ZRO coin, or only Aptos coin 133 | 134 | It is very simple to use it by initializing it by calling `fun init(account: &signer, cap: UaCapability)` in your UA. 135 | 136 | ## Examples: 137 | - [OmniCounter](counter/sources/counter.move) 138 | - [TokenBridge](bridge/sources/bridge.move) -------------------------------------------------------------------------------- /sdk/src/modules/uln/uln_signer.ts: -------------------------------------------------------------------------------- 1 | import { SDK } from "../../index" 2 | import * as aptos from "aptos" 3 | import { isErrorOfApiError, MultipleSignFunc } from "../../utils" 4 | import { UlnSignerFee } from "../../types" 5 | 6 | export class UlnSigner { 7 | public readonly module 8 | public readonly moduleName 9 | 10 | constructor(private sdk: SDK) { 11 | this.module = `${sdk.accounts.layerzero}::uln_signer` 12 | this.moduleName = "layerzero::uln_signer" 13 | } 14 | 15 | async register_TransactionPayload(): Promise { 16 | return new aptos.TxnBuilderTypes.TransactionPayloadEntryFunction( 17 | aptos.TxnBuilderTypes.EntryFunction.natural(`${this.module}`, "register", [], []), 18 | ) 19 | } 20 | 21 | async registerMS( 22 | multisigAccountAddress: string, 23 | multisigAccountPubkey: aptos.TxnBuilderTypes.MultiEd25519PublicKey, 24 | signFunc: MultipleSignFunc, 25 | ): Promise { 26 | const payload = await this.register_TransactionPayload() 27 | return await this.sdk.sendAndConfirmMultiSigTransaction( 28 | this.sdk.client, 29 | multisigAccountAddress, 30 | multisigAccountPubkey, 31 | payload, 32 | signFunc, 33 | ) 34 | } 35 | 36 | async isRegistered(address: aptos.MaybeHexString): Promise { 37 | try { 38 | await this.sdk.client.getAccountResource(address, `${this.module}::Config`) 39 | return true 40 | } catch (e) { 41 | if (isErrorOfApiError(e, 404)) { 42 | return false 43 | } 44 | throw e 45 | } 46 | } 47 | 48 | registerPayload(): aptos.Types.EntryFunctionPayload { 49 | return { 50 | function: `${this.module}::register`, 51 | type_arguments: [], 52 | arguments: [], 53 | } 54 | } 55 | 56 | async register(signer: aptos.AptosAccount): Promise { 57 | const transaction = this.registerPayload() 58 | return this.sdk.sendAndConfirmTransaction(signer, transaction) 59 | } 60 | 61 | async getSetFee_TransactionPayload( 62 | dstChainId: aptos.BCS.Uint16, 63 | baseFee: aptos.BCS.Uint64 | aptos.BCS.Uint32, 64 | feePerByte: aptos.BCS.Uint64 | aptos.BCS.Uint32, 65 | ): Promise { 66 | return new aptos.TxnBuilderTypes.TransactionPayloadEntryFunction( 67 | aptos.TxnBuilderTypes.EntryFunction.natural( 68 | `${this.module}`, 69 | "set_fee", 70 | [], 71 | [ 72 | aptos.BCS.bcsSerializeUint64(dstChainId), 73 | aptos.BCS.bcsSerializeUint64(baseFee), 74 | aptos.BCS.bcsSerializeUint64(feePerByte), 75 | ], 76 | ), 77 | ) 78 | } 79 | 80 | setFeePayload( 81 | dstChainId: aptos.BCS.Uint16, 82 | baseFee: aptos.BCS.Uint64 | aptos.BCS.Uint32, 83 | feePerByte: aptos.BCS.Uint64 | aptos.BCS.Uint32, 84 | ): aptos.Types.EntryFunctionPayload { 85 | return { 86 | function: `${this.module}::set_fee`, 87 | type_arguments: [], 88 | arguments: [dstChainId, baseFee, feePerByte], 89 | } 90 | } 91 | 92 | async setFee( 93 | signer: aptos.AptosAccount, 94 | dstChainId: aptos.BCS.Uint16, 95 | baseFee: aptos.BCS.Uint64 | aptos.BCS.Uint32, 96 | feePerByte: aptos.BCS.Uint64 | aptos.BCS.Uint32, 97 | ): Promise { 98 | const transaction = this.setFeePayload(dstChainId, baseFee, feePerByte) 99 | return this.sdk.sendAndConfirmTransaction(signer, transaction) 100 | } 101 | 102 | async setFeeMS( 103 | multisigAccountAddress: string, 104 | multisigAccountPubkey: aptos.TxnBuilderTypes.MultiEd25519PublicKey, 105 | signFunc: MultipleSignFunc, 106 | dstChainId: aptos.BCS.Uint16, 107 | baseFee: aptos.BCS.Uint64 | aptos.BCS.Uint32, 108 | feePerByte: aptos.BCS.Uint64 | aptos.BCS.Uint32, 109 | ): Promise { 110 | const payload = await this.getSetFee_TransactionPayload(dstChainId, baseFee, feePerByte) 111 | return await this.sdk.sendAndConfirmMultiSigTransaction( 112 | this.sdk.client, 113 | multisigAccountAddress, 114 | multisigAccountPubkey, 115 | payload, 116 | signFunc, 117 | ) 118 | } 119 | 120 | async getFee(address: aptos.MaybeHexString, dstChainId: aptos.BCS.Uint16): Promise { 121 | try { 122 | const resource = await this.sdk.client.getAccountResource(address, `${this.module}::Config`) 123 | const { fees } = resource.data as { fees: { handle: string } } 124 | const response = await this.sdk.client.getTableItem(fees.handle, { 125 | key_type: `u64`, 126 | value_type: `${this.module}::Fee`, 127 | key: dstChainId.toString(), 128 | }) 129 | return { 130 | base_fee: BigInt(response.base_fee), 131 | fee_per_byte: BigInt(response.fee_per_byte), 132 | } 133 | } catch (e) { 134 | if (isErrorOfApiError(e, 404)) { 135 | return { 136 | base_fee: BigInt(0), 137 | fee_per_byte: BigInt(0), 138 | } 139 | } 140 | throw e 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /sdk/src/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { decodePacket, encodePacket, hashPacket } from "./utils" 2 | 3 | describe("test common functions for aptos", () => { 4 | const expectedPacket = { 5 | src_chain_id: 10108, 6 | src_address: Buffer.from("33a4cf3cabceb43904ec7609221ff391e682ec81d13a89f68a356885d28995b8", "hex"), 7 | dst_chain_id: 10121, 8 | dst_address: Buffer.from("2afd0d8a477ad393d2234253407fb1cec92749d1", "hex"), 9 | nonce: 1n, 10 | payload: Buffer.from( 11 | "01000000000000000000000000cc0235a403e77c56d0f271054ad8bd3abcd21904000000000000000000000000b062bfe01b1cf94ffe7618657c0a7b23b909dbe10000000000004e2001", 12 | "hex", 13 | ), 14 | } 15 | 16 | const expectedEvent = { 17 | version: "12767", 18 | key: "0x0600000000000000dcf30c9b54f8a181953b6c906d14373b52da7d9b467c24720d8744dea3f3f580", 19 | sequence_number: "0", 20 | type: "0xdcf30c9b54f8a181953b6c906d14373b52da7d9b467c24720d8744dea3f3f580::packet_event::OutboundEvent", 21 | data: { 22 | encoded_packet: 23 | "0x0000000000000001277c33a4cf3cabceb43904ec7609221ff391e682ec81d13a89f68a356885d28995b827892afd0d8a477ad393d2234253407fb1cec92749d101000000000000000000000000cc0235a403e77c56d0f271054ad8bd3abcd21904000000000000000000000000b062bfe01b1cf94ffe7618657c0a7b23b909dbe10000000000004e2001", 24 | }, 25 | } 26 | 27 | test("test event", async () => { 28 | const hexValue = expectedEvent.data.encoded_packet.replace(/^0x/, "") 29 | const input = Buffer.from(hexValue, "hex") 30 | const packet = await decodePacket(input, 20) 31 | const output = encodePacket(packet) 32 | expect(output).toEqual(input) 33 | }) 34 | 35 | test("test packet", async () => { 36 | const input = encodePacket(expectedPacket) 37 | const packet = await decodePacket(input, 20) 38 | expect(packet.nonce).toEqual(expectedPacket.nonce) 39 | expect(packet.src_chain_id).toEqual(expectedPacket.src_chain_id) 40 | expect(packet.src_address).toEqual(expectedPacket.src_address) 41 | expect(packet.dst_chain_id).toEqual(expectedPacket.dst_chain_id) 42 | expect(packet.dst_address).toEqual(expectedPacket.dst_address) 43 | expect(packet.payload).toEqual(expectedPacket.payload) 44 | }) 45 | 46 | test("test hashPacket", async () => { 47 | const packet = await decodePacket(Buffer.from(expectedEvent.data.encoded_packet.replace(/^0x/, ""), "hex"), 32) 48 | const hash = hashPacket(packet) 49 | expect(hash).toEqual("dfc7bfef9ecd6986f7d56e5731ad11239a14137db78ba0fc108b4e96893036a0") 50 | }) 51 | 52 | test("output hash", () => { 53 | const expectedPacket = { 54 | src_chain_id: "10121", 55 | src_address: Buffer.from([ 56 | 42, 253, 13, 138, 71, 122, 211, 147, 210, 35, 66, 83, 64, 127, 177, 206, 201, 39, 73, 209, 57 | ]), 58 | dst_chain_id: "10108", 59 | dst_address: Buffer.from([ 60 | 51, 164, 207, 60, 171, 206, 180, 57, 4, 236, 118, 9, 34, 31, 243, 145, 230, 130, 236, 129, 209, 58, 137, 61 | 246, 138, 53, 104, 133, 210, 137, 149, 184, 62 | ]), 63 | nonce: "1", 64 | payload: Buffer.from([ 65 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 2, 53, 164, 3, 231, 124, 86, 208, 242, 113, 5, 74, 216, 189, 66 | 58, 188, 210, 25, 4, 20, 190, 116, 8, 8, 137, 220, 129, 182, 20, 89, 147, 104, 117, 222, 57, 69, 252, 67 | 84, 206, 4, 222, 220, 135, 234, 199, 212, 188, 102, 84, 182, 229, 0, 0, 0, 0, 0, 15, 66, 64, 68 | ]), 69 | } 70 | 71 | const data = encodePacket(expectedPacket) 72 | expect(data.length).toBe(137) 73 | 74 | const hash = hashPacket(expectedPacket) 75 | expect(typeof hash === "string").toBeTruthy() 76 | expect(hash.length).toBe(64) 77 | expect(hash).toMatch(/^[0-9a-f]+$/) 78 | }) 79 | 80 | test("Object to array", () => { 81 | const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 82 | { 83 | expect(array instanceof Uint8Array).toBeFalsy() 84 | expect(array instanceof Buffer).toBeFalsy() 85 | expect(array instanceof Array).toBeTruthy() 86 | expect(array instanceof Object).toBeTruthy() 87 | 88 | const object = JSON.parse(JSON.stringify(array)) 89 | expect(object instanceof Uint8Array).toBeFalsy() 90 | expect(object instanceof Buffer).toBeFalsy() 91 | expect(object instanceof Array).toBeTruthy() 92 | expect(object instanceof Object).toBeTruthy() 93 | } 94 | 95 | const uint8Array = Uint8Array.from(array) 96 | { 97 | expect(uint8Array instanceof Uint8Array).toBeTruthy() 98 | expect(uint8Array instanceof Buffer).toBeFalsy() 99 | expect(uint8Array instanceof Array).toBeFalsy() 100 | expect(uint8Array instanceof Object).toBeFalsy() 101 | 102 | const object = JSON.parse(JSON.stringify(uint8Array)) 103 | expect(object instanceof Uint8Array).toBeFalsy() 104 | expect(object instanceof Buffer).toBeFalsy() 105 | expect(object instanceof Array).toBeFalsy() 106 | expect(object instanceof Object).toBeTruthy() 107 | 108 | const buf = Buffer.from(uint8Array) 109 | expect(buf instanceof Uint8Array).toBeTruthy() 110 | expect(buf instanceof Buffer).toBeTruthy() 111 | expect(buf instanceof Array).toBeFalsy() 112 | expect(buf instanceof Object).toBeFalsy() 113 | } 114 | }) 115 | }) 116 | -------------------------------------------------------------------------------- /apps/bridge/sources/limiter.move: -------------------------------------------------------------------------------- 1 | module bridge::limiter { 2 | use aptos_framework::timestamp; 3 | use std::error; 4 | use aptos_std::math64::{pow}; 5 | 6 | friend bridge::coin_bridge; 7 | 8 | const EBRIDGE_CAP_OVERFLOW: u64 = 0x00; 9 | 10 | struct Limiter has key { 11 | enabled: bool, 12 | // time in seconds 13 | t0_sec: u64, 14 | window_sec: u64, 15 | // total outflow 16 | sum_sd: u64, 17 | // cap 18 | cap_sd: u64, 19 | } 20 | 21 | public(friend) fun register_coin(account: &signer, cap_sd: u64) { 22 | // only call once upon coin registration 23 | move_to(account, Limiter { 24 | enabled: true, 25 | t0_sec: timestamp::now_seconds(), 26 | sum_sd: 0, 27 | cap_sd, 28 | window_sec: 3600 * 4, // default 4 hours 29 | }) 30 | } 31 | 32 | public(friend) fun set_limiter(enabled: bool, cap_sd: u64, window_sec: u64) acquires Limiter { 33 | let twa = borrow_global_mut>(@bridge); 34 | twa.enabled = enabled; 35 | twa.cap_sd = cap_sd; 36 | twa.window_sec = window_sec; 37 | } 38 | 39 | // new window, inherit half of the prior sum 40 | // a simple way to approximate a sliding window 41 | public(friend) fun try_insert(amount_sd: u64) acquires Limiter { 42 | let limiter = borrow_global_mut>(@bridge); 43 | if (!limiter.enabled) return; 44 | 45 | let now = timestamp::now_seconds(); 46 | let count = (now - limiter.t0_sec) / limiter.window_sec; 47 | if (count > 0) { 48 | limiter.t0_sec = limiter.t0_sec + limiter.window_sec * count; 49 | if (count >= 64) { 50 | limiter.sum_sd = 0; 51 | } else { 52 | limiter.sum_sd = limiter.sum_sd / pow(2, count); 53 | } 54 | }; 55 | 56 | limiter.sum_sd = limiter.sum_sd + amount_sd; 57 | assert!(limiter.sum_sd <= limiter.cap_sd, error::out_of_range(EBRIDGE_CAP_OVERFLOW)); 58 | } 59 | 60 | #[test_only] 61 | struct FakeCoin {} 62 | 63 | #[test(aptos_framework = @aptos_framework, bridge = @bridge)] 64 | fun test_limiter(aptos_framework: &signer, bridge: &signer) acquires Limiter { 65 | use aptos_framework::aptos_account; 66 | use std::signer; 67 | use aptos_framework::timestamp; 68 | 69 | aptos_account::create_account(signer::address_of(bridge)); 70 | 71 | // set global time 72 | timestamp::set_time_has_started_for_testing(aptos_framework); 73 | let time = 1000; 74 | timestamp::update_global_time_for_test_secs(time); 75 | 76 | register_coin(bridge, 10000); 77 | try_insert(5000); 78 | 79 | // in the same time window 80 | let time = time + 2 * 3600; // half of the window 81 | timestamp::update_global_time_for_test_secs(time); 82 | let twa = borrow_global>(@bridge); 83 | assert!(twa.sum_sd == 5000, 0); 84 | 85 | // in the next time window 86 | let time = time + 2 * 3600; // half of the window 87 | timestamp::update_global_time_for_test_secs(time); // 4 hours later 88 | try_insert(1000); 89 | let twa = borrow_global>(@bridge); 90 | assert!(twa.sum_sd == 3500, 0); // 2500 + 1000 91 | 92 | // in the next 3 time window 93 | let time = time + 4 * 3600 * 2; 94 | timestamp::update_global_time_for_test_secs(time); 95 | try_insert(0); 96 | let twa = borrow_global>(@bridge); 97 | assert!(twa.sum_sd == 3500 / 4, 0); 98 | } 99 | 100 | #[test(aptos_framework = @aptos_framework, bridge = @bridge)] 101 | #[expected_failure(abort_code = 0x20000, location = Self)] 102 | fun test_limiter_overflow(aptos_framework: &signer, bridge: &signer) acquires Limiter { 103 | use aptos_framework::aptos_account; 104 | use std::signer; 105 | use aptos_framework::timestamp; 106 | 107 | aptos_account::create_account(signer::address_of(bridge)); 108 | 109 | // set global time 110 | timestamp::set_time_has_started_for_testing(aptos_framework); 111 | timestamp::update_global_time_for_test_secs(1000); 112 | 113 | register_coin(bridge, 10000); 114 | 115 | try_insert(10001); 116 | } 117 | 118 | #[test(aptos_framework = @aptos_framework, bridge = @bridge)] 119 | #[expected_failure(abort_code = 0x20000, location = Self)] 120 | fun test_limiter_overflow2(aptos_framework: &signer, bridge: &signer) acquires Limiter { 121 | use aptos_framework::aptos_account; 122 | use std::signer; 123 | use aptos_framework::timestamp; 124 | 125 | aptos_account::create_account(signer::address_of(bridge)); 126 | 127 | // set global time 128 | timestamp::set_time_has_started_for_testing(aptos_framework); 129 | let time = 1000; 130 | timestamp::update_global_time_for_test_secs(time); 131 | 132 | register_coin(bridge, 10000); 133 | try_insert(5000); 134 | 135 | // in the next time window 136 | let time = time + 4 * 3600; // half of the window 137 | timestamp::update_global_time_for_test_secs(time); // 4 hours later 138 | try_insert(1000); 139 | let twa = borrow_global>(@bridge); 140 | assert!(twa.sum_sd == 3500, 0); // 2500 + 1000 141 | 142 | // overflow 143 | try_insert(7000); 144 | } 145 | } -------------------------------------------------------------------------------- /layerzero/sources/msglib-v1/v1-0/uln_signer.move: -------------------------------------------------------------------------------- 1 | // a signer can be a multi-sig or resources account 2 | // the implemention of signer can be arbitrary. 3 | module layerzero::uln_signer { 4 | use aptos_std::table::{Self, Table}; 5 | use layerzero_common::utils::assert_u16; 6 | use std::signer::address_of; 7 | use layerzero_common::acl::{Self, ACL}; 8 | use std::error; 9 | 10 | const EULN_SIGNER_NO_CONFIG: u64 = 0x00; 11 | const EULN_SIGNER_NOT_REGISTERED: u64 = 0x01; 12 | const EULN_SIGNER_ALREADY_REGISTERED: u64 = 0x02; 13 | 14 | struct Fee has store, drop { 15 | base_fee: u64, 16 | fee_per_byte: u64 17 | } 18 | 19 | struct Config has key { 20 | fees: Table, 21 | acl: ACL 22 | } 23 | 24 | // 25 | // signer functions 26 | // 27 | public entry fun register(account: &signer) { 28 | assert!(!exists(address_of(account)), error::already_exists(EULN_SIGNER_ALREADY_REGISTERED)); 29 | 30 | move_to(account, Config { 31 | fees: table::new(), 32 | acl: acl::empty() 33 | }); 34 | } 35 | 36 | public entry fun set_fee(account: &signer, remote_chain_id: u64, base_fee: u64, fee_per_byte: u64) acquires Config { 37 | assert_u16(remote_chain_id); 38 | 39 | let account_addr = address_of(account); 40 | assert_signer_registered(account_addr); 41 | 42 | let config = borrow_global_mut(account_addr); 43 | table::upsert(&mut config.fees, remote_chain_id, Fee { 44 | base_fee, 45 | fee_per_byte 46 | }); 47 | } 48 | 49 | /// if not in the allow list, add it. Otherwise, remove it. 50 | public entry fun allowlist(account: &signer, ua: address) acquires Config { 51 | let account_addr = address_of(account); 52 | assert_signer_registered(account_addr); 53 | 54 | let config = borrow_global_mut(account_addr); 55 | acl::allowlist(&mut config.acl, ua); 56 | } 57 | 58 | /// if not in the deny list, add it. Otherwise, remove it. 59 | public entry fun denylist(account: &signer, ua: address) acquires Config { 60 | let account_addr = address_of(account); 61 | assert_signer_registered(account_addr); 62 | 63 | let config = borrow_global_mut(account_addr); 64 | acl::denylist(&mut config.acl, ua); 65 | } 66 | 67 | 68 | // 69 | // view functions 70 | // 71 | public fun quote(uln_signer: address, ua: address, remote_chain_id: u64, payload_size: u64): u64 acquires Config { 72 | assert_signer_registered(uln_signer); 73 | 74 | let config = borrow_global(uln_signer); 75 | 76 | acl::assert_allowed(&config.acl, &ua); 77 | 78 | assert!(table::contains(&config.fees, remote_chain_id), EULN_SIGNER_NO_CONFIG); 79 | let fee = table::borrow(&config.fees, remote_chain_id); 80 | fee.fee_per_byte * payload_size + fee.base_fee 81 | } 82 | 83 | public fun check_permission(uln_signer: address, ua: address): bool acquires Config { 84 | assert_signer_registered(uln_signer); 85 | 86 | let config = borrow_global(uln_signer); 87 | acl::is_allowed(&config.acl, &ua) 88 | } 89 | 90 | fun assert_signer_registered(account: address) { 91 | assert!(exists(account), EULN_SIGNER_NOT_REGISTERED); 92 | } 93 | 94 | #[test_only] 95 | fun setup(lz: &signer, uln_singer: &signer) { 96 | use std::signer; 97 | use aptos_framework::aptos_account; 98 | 99 | aptos_account::create_account(signer::address_of(lz)); 100 | aptos_account::create_account(signer::address_of(uln_singer)); 101 | 102 | register(uln_singer); 103 | } 104 | 105 | #[test(lz = @layerzero, uln_singer = @1234)] 106 | fun test_acl(lz: signer, uln_singer: signer) acquires Config { 107 | setup(&lz, &uln_singer); 108 | 109 | let alice = @1122; 110 | let bob = @3344; 111 | let carol = @5566; 112 | 113 | // by default, all accounts are permitted 114 | assert!(check_permission(address_of(&uln_singer), alice), 0); 115 | assert!(check_permission(address_of(&uln_singer), bob), 0); 116 | assert!(check_permission(address_of(&uln_singer), carol), 0); 117 | 118 | // allow alice, deny bob 119 | allowlist(&uln_singer, alice); 120 | denylist(&uln_singer, bob); 121 | assert!(check_permission(address_of(&uln_singer), alice), 0); 122 | assert!(!check_permission(address_of(&uln_singer), bob), 0); 123 | assert!(!check_permission(address_of(&uln_singer), carol), 0); // carol is not in the allow list 124 | 125 | // allow carol, now he is also permitted, to test we can allow more than 1 account 126 | allowlist(&uln_singer, carol); 127 | assert!(check_permission(address_of(&uln_singer), carol), 0); 128 | 129 | // remove carol from the whitelist. not permitted again 130 | allowlist(&uln_singer, carol); 131 | assert!(!check_permission(address_of(&uln_singer), carol), 0); 132 | 133 | // remove alice, now alice and carol are permitted but not bob 134 | allowlist(&uln_singer, alice); 135 | assert!(check_permission(address_of(&uln_singer), alice), 0); 136 | assert!(!check_permission(address_of(&uln_singer), bob), 0); 137 | assert!(check_permission(address_of(&uln_singer), carol), 0); 138 | } 139 | 140 | #[test(lz = @layerzero, uln_singer = @1234)] 141 | fun test_quote_fee(lz: signer, uln_singer: signer) acquires Config { 142 | setup(&lz, &uln_singer); 143 | 144 | set_fee(&uln_singer, 1, 100, 10); 145 | assert!(quote(address_of(&uln_singer), @1122, 1, 100) == 1100, 0); 146 | } 147 | } -------------------------------------------------------------------------------- /sdk/tasks/config/sandbox.ts: -------------------------------------------------------------------------------- 1 | import { CHAIN_KEY, ChainId, ChainStage } from "@layerzerolabs/core-sdk" 2 | import { evmBridgeAddresses, TOKEN_ADDRESSES } from "../../../apps/bridge-evm/tasks/config/addresses" 3 | import { applyArbitrumMultiplier, ConfigType, DEFAULT_BLOCK_CONFIRMATIONS, EVM_ADDERSS_SIZE } from "./common" 4 | import { CoinType } from "../../src/modules/apps/coin" 5 | import { DEFAULT_LIMITER_CAP_SD, DEFAULT_LIMITER_WINDOW_SEC, PacketType } from "../../src/modules/apps/bridge" 6 | import { 7 | BRIDGE_ADDRESS, 8 | EXECUTOR_ADDRESS, 9 | ORACLE_ADDRESS, 10 | ORACLE_SIGNER_ADDRESS, 11 | RELAYER_SIGNER_ADDRESS, 12 | } from "../../src/constants" 13 | 14 | const chainStage = ChainStage.TESTNET_SANDBOX 15 | const ChainIds = [ChainId.GOERLI_SANDBOX] 16 | 17 | const _CONFIG: ConfigType = { 18 | msglib: { 19 | msglibv1: { 20 | addressSize: 20, 21 | versionTypes: { 22 | "1.0": "uln_receive::ULN", 23 | }, 24 | defaultAppConfig: {}, 25 | defaultSendVersion: "1.0", 26 | defaultReceiveVersion: "1.0", 27 | }, 28 | }, 29 | endpoint: { 30 | defaultExecutorVersion: 1, 31 | defaultExecutor: { 32 | version: 1, 33 | address: EXECUTOR_ADDRESS[chainStage], 34 | }, 35 | defaultAdapterParam: {}, 36 | }, 37 | executor: { 38 | address: EXECUTOR_ADDRESS[chainStage], 39 | fee: {}, 40 | }, 41 | relayer: { 42 | signerAddress: RELAYER_SIGNER_ADDRESS[chainStage], 43 | fee: {}, 44 | }, 45 | oracle: { 46 | address: ORACLE_ADDRESS[chainStage], 47 | signerAddress: ORACLE_SIGNER_ADDRESS[chainStage], 48 | fee: {}, 49 | validators: { 50 | "0xbe08c77d93f1560132d2d78b2aab2a5559adeee897bcdea25de754a34f36c7f7": true, 51 | "0x96cf17279932c8c837751b5e1d01b7d7106eeab4928b8e25fcf4acbfb5bbdb4d": true, 52 | "0xc98b5018c54451bd3a04ccdc86ab76ac474ba90a29f94678e9d109cd24b15772": true, 53 | }, 54 | threshold: 1, 55 | }, 56 | bridge: { 57 | address: BRIDGE_ADDRESS[chainStage], 58 | enableCustomAdapterParams: true, 59 | remoteBridge: {}, 60 | minDstGas: { 61 | [PacketType.SEND]: {}, 62 | }, 63 | coins: { 64 | [CoinType.WETH]: { 65 | name: "Wrapped Ether", 66 | symbol: "WETH", 67 | decimals: 6, 68 | remotes: { 69 | [ChainId.GOERLI_SANDBOX]: {}, 70 | }, 71 | limiter: { 72 | enabled: true, 73 | capSD: DEFAULT_LIMITER_CAP_SD, 74 | windowSec: DEFAULT_LIMITER_WINDOW_SEC, 75 | }, 76 | }, 77 | [CoinType.USDC]: { 78 | name: "USD Coin", 79 | symbol: "USDC", 80 | decimals: 6, 81 | remotes: { 82 | [ChainId.GOERLI_SANDBOX]: {}, 83 | }, 84 | limiter: { 85 | enabled: true, 86 | capSD: DEFAULT_LIMITER_CAP_SD, 87 | windowSec: DEFAULT_LIMITER_WINDOW_SEC, 88 | }, 89 | }, 90 | }, 91 | }, 92 | } 93 | 94 | export function getConfig(): ConfigType { 95 | for (const chainId of ChainIds) { 96 | // fill default app config for each chain 97 | _CONFIG.msglib.msglibv1.defaultAppConfig[chainId] = {} 98 | _CONFIG.msglib.msglibv1.defaultAppConfig[chainId].inboundConfirmations = 99 | DEFAULT_BLOCK_CONFIRMATIONS[chainStage][chainId] 100 | _CONFIG.msglib.msglibv1.defaultAppConfig[chainId].oracle = _CONFIG.oracle.signerAddress 101 | _CONFIG.msglib.msglibv1.defaultAppConfig[chainId].outboundConfirmations = 10 //only aptos 102 | _CONFIG.msglib.msglibv1.defaultAppConfig[chainId].relayer = RELAYER_SIGNER_ADDRESS[chainStage] 103 | 104 | // fill bridge config 105 | _CONFIG.bridge.remoteBridge[chainId] = {} 106 | _CONFIG.bridge.remoteBridge[chainId].address = evmBridgeAddresses(CHAIN_KEY[chainId]) 107 | _CONFIG.bridge.remoteBridge[chainId].addressSize = EVM_ADDERSS_SIZE 108 | _CONFIG.bridge.minDstGas[PacketType.SEND][chainId] = applyArbitrumMultiplier(chainId, 150000) 109 | 110 | //fill relayer fee 111 | _CONFIG.relayer.fee[chainId] = {} 112 | _CONFIG.relayer.fee[chainId].baseFee = applyArbitrumMultiplier(chainId, 100) 113 | _CONFIG.relayer.fee[chainId].feePerByte = 1 114 | 115 | //fill oracle fee 116 | _CONFIG.oracle.fee[chainId] = {} 117 | _CONFIG.oracle.fee[chainId].baseFee = applyArbitrumMultiplier(chainId, 100) 118 | 119 | //fill endpoint default adapter param 120 | _CONFIG.endpoint.defaultAdapterParam[chainId] = {} 121 | _CONFIG.endpoint.defaultAdapterParam[chainId].uaGas = applyArbitrumMultiplier(chainId, 200000) 122 | 123 | //fill executor 124 | _CONFIG.executor.fee[chainId] = {} 125 | _CONFIG.executor.fee[chainId].airdropAmtCap = applyArbitrumMultiplier(chainId, 10000000000) 126 | _CONFIG.executor.fee[chainId].priceRatio = 10000000000 127 | _CONFIG.executor.fee[chainId].gasPrice = 1 128 | } 129 | 130 | // fill coin config 131 | for (const coinType in _CONFIG.bridge.coins) { 132 | for (const remoteChainId in _CONFIG.bridge.coins[coinType].remotes) { 133 | _CONFIG.bridge.coins[coinType].remotes[remoteChainId].address = TOKEN_ADDRESSES[coinType][remoteChainId] 134 | _CONFIG.bridge.coins[coinType].remotes[remoteChainId].unwrappable = coinType === CoinType.WETH 135 | } 136 | } 137 | return _CONFIG 138 | } 139 | -------------------------------------------------------------------------------- /sdk/tasks/utils.ts: -------------------------------------------------------------------------------- 1 | import { CHAIN_STAGE, ChainKey, ChainStage } from "@layerzerolabs/core-sdk" 2 | import { getAccount, KeyType } from "../src/utils" 3 | import * as aptos from "aptos" 4 | import invariant from "tiny-invariant" 5 | import * as util from "util" 6 | import * as child_process from "child_process" 7 | import * as fs from "fs" 8 | import * as path from "path" 9 | 10 | const INCLUDE_ARTIFACTS = "none" //none,sparse,all 11 | 12 | export const ZRO_MODULES = ["zro.mv"] 13 | 14 | export const LAYERZERO_COMMON_MODULES = ["acl.mv", "utils.mv", "serde.mv", "packet.mv", "semver.mv"] 15 | 16 | export const MSGLIB_AUTH_MODULES = ["msglib_cap.mv"] 17 | export const MSGLIB_V1_1_MODUELS = ["msglib_v1_1.mv", "msglib_v1_1_router.mv"] 18 | export const MSGLIB_V2_MODUELS = ["msglib_v2_router.mv"] 19 | 20 | export const EXECUTOR_AUTH_MODULES = ["executor_cap.mv"] 21 | export const EXECUTOR_V2_MODULES = ["executor_v2.mv"] 22 | 23 | export const LAYERZERO_MODULES = [ 24 | "admin.mv", 25 | "bulletin.mv", 26 | "channel.mv", 27 | "uln_signer.mv", 28 | "uln_config.mv", 29 | "packet_event.mv", 30 | "msglib_v1_0.mv", 31 | "msglib_router.mv", 32 | "msglib_config.mv", 33 | "executor_v1.mv", 34 | "executor_router.mv", 35 | "executor_config.mv", 36 | "endpoint.mv", 37 | "lzapp.mv", 38 | "remote.mv", 39 | "oft.mv", 40 | "uln_receive.mv", 41 | ] 42 | 43 | export const COUNTER_MODULES = ["counter.mv"] 44 | 45 | export const BRIDGE_MODULES = ["asset.mv", "limiter.mv", "coin_bridge.mv"] 46 | 47 | export const ORACLE_MODULES = ["oracle.mv"] 48 | 49 | function getNetworkForStage(stage: ChainStage) { 50 | const networks: string[] = [] 51 | for (const keyType in ChainKey) { 52 | const key = ChainKey[keyType as keyof typeof ChainKey] 53 | if (CHAIN_STAGE[key] === stage) { 54 | networks.push(key) 55 | } 56 | } 57 | return networks 58 | } 59 | 60 | export function validateStageOfNetworks(stage: ChainStage, toNetworks: string[]) { 61 | const networks = getNetworkForStage(stage) 62 | toNetworks.forEach((network) => { 63 | if (!networks.includes(network)) { 64 | throw new Error(`Invalid network: ${network} for stage: ${stage}`) 65 | } 66 | }) 67 | } 68 | 69 | export function getAccountFromFile(file: string) { 70 | return getAccount(file, KeyType.JSON_FILE) 71 | } 72 | 73 | export function getAccountFromFileFromMnemonic(file: string) { 74 | return getAccount(file, KeyType.MNEMONIC) 75 | } 76 | 77 | export function semanticVersion(version: string): { major: aptos.BCS.Uint64; minor: aptos.BCS.Uint8 } { 78 | const v = version.split(".") 79 | invariant(v.length === 2, "Invalid version format") 80 | return { 81 | major: BigInt(v[0]), 82 | minor: Number(v[1]), 83 | } 84 | } 85 | 86 | export async function getDeployedModules(client: aptos.AptosClient, address: aptos.HexString): Promise { 87 | const modules = await client.getAccountModules(address) 88 | return modules 89 | .map((m) => { 90 | return m.abi 91 | }) 92 | .filter((m) => m !== undefined) 93 | .map((m) => { 94 | return m!.name 95 | }) 96 | } 97 | 98 | export async function initialDeploy( 99 | client: aptos.AptosClient, 100 | address: aptos.HexString, 101 | moduleNames: string[], 102 | ): Promise { 103 | const checkModules = moduleNames.map((m) => m.replace(".mv", "")) 104 | const accountModules = await getDeployedModules(client, address) 105 | const modules = accountModules.filter((m) => checkModules.includes(m)) 106 | return modules.length === 0 107 | } 108 | 109 | export async function compilePackage( 110 | packagePath: string, 111 | buildPath: string, 112 | namedAddresses: { [key: string]: string }, 113 | ) { 114 | const addresses = Object.keys(namedAddresses) 115 | .map((key) => `${key}=${namedAddresses[key]}`) 116 | .join(",") 117 | const command = `aptos move compile --included-artifacts ${INCLUDE_ARTIFACTS} --save-metadata --package-dir ${packagePath} --output-dir ${buildPath} --named-addresses ${addresses}` 118 | console.log(`command: ${command}`) 119 | const execPromise = util.promisify(child_process.exec) 120 | return execPromise(command) 121 | } 122 | 123 | export function getMetadataAndModules( 124 | buildPath: string, 125 | moduleNames: string[], 126 | ): { metadata: Uint8Array; modules: aptos.TxnBuilderTypes.Module[] } { 127 | const dirName = buildPath.split("/").pop().replace(new RegExp("-", "g"), "_") 128 | const metadataPath = path.join(buildPath, `build/${dirName}/package-metadata.bcs`) 129 | const modulePath = path.join(buildPath, `build/${dirName}/bytecode_modules`) 130 | const metadata = Uint8Array.from(fs.readFileSync(metadataPath)) 131 | const modules = moduleNames.map( 132 | (f) => new aptos.TxnBuilderTypes.Module(Uint8Array.from(fs.readFileSync(path.join(modulePath, f)))), 133 | ) 134 | return { metadata, modules } 135 | } 136 | 137 | export function arrayToCsv(columns, data) { 138 | return columns 139 | .join(",") 140 | .concat("\n") 141 | .concat( 142 | data 143 | .map( 144 | (row) => 145 | row 146 | .map(String) // convert every value to String 147 | .map((v) => (v === "undefined" ? "" : v)) 148 | .map((v) => v.replace(/\"/g, "\"\"")) // escape double colons 149 | .map((v) => `"${v}"`) // quote it 150 | .join(","), // comma-separated 151 | ) 152 | .join("\r\n"), // rows starting on new lines) 153 | ) 154 | } 155 | --------------------------------------------------------------------------------