Add TokenBridge deployment (#1215)
* Add TokenBridge deployment * PR review * Remove only * Fix hardhat * Fix ethers * PR review * PR review * lint * Undo merging into core addressespull/1228/head
parent
7f54dcbac3
commit
fd54116155
@ -0,0 +1,38 @@ |
|||||||
|
// SPDX-License-Identifier: Apache-2.0 |
||||||
|
pragma solidity ^0.8.13; |
||||||
|
|
||||||
|
import {ICircleBridge} from "../middleware/token-bridge/interfaces/circle/ICircleBridge.sol"; |
||||||
|
import {MockToken} from "./MockToken.sol"; |
||||||
|
|
||||||
|
contract MockCircleBridge is ICircleBridge { |
||||||
|
uint64 public nextNonce = 0; |
||||||
|
MockToken token; |
||||||
|
|
||||||
|
constructor(MockToken _token) { |
||||||
|
token = _token; |
||||||
|
} |
||||||
|
|
||||||
|
function depositForBurn( |
||||||
|
uint256 _amount, |
||||||
|
uint32, |
||||||
|
bytes32, |
||||||
|
address _burnToken |
||||||
|
) external returns (uint64 _nonce) { |
||||||
|
nextNonce = nextNonce + 1; |
||||||
|
_nonce = nextNonce; |
||||||
|
require(address(token) == _burnToken); |
||||||
|
token.transferFrom(msg.sender, address(this), _amount); |
||||||
|
token.burn(_amount); |
||||||
|
} |
||||||
|
|
||||||
|
function depositForBurnWithCaller( |
||||||
|
uint256, |
||||||
|
uint32, |
||||||
|
bytes32, |
||||||
|
address, |
||||||
|
bytes32 |
||||||
|
) external returns (uint64 _nonce) { |
||||||
|
nextNonce = nextNonce + 1; |
||||||
|
_nonce = nextNonce; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
// SPDX-License-Identifier: Apache-2.0 |
||||||
|
pragma solidity ^0.8.13; |
||||||
|
|
||||||
|
import {ICircleMessageTransmitter} from "../middleware/token-bridge/interfaces/circle/ICircleMessageTransmitter.sol"; |
||||||
|
import {MockToken} from "./MockToken.sol"; |
||||||
|
|
||||||
|
contract MockCircleMessageTransmitter is ICircleMessageTransmitter { |
||||||
|
mapping(bytes32 => bool) processedNonces; |
||||||
|
MockToken token; |
||||||
|
|
||||||
|
constructor(MockToken _token) { |
||||||
|
token = _token; |
||||||
|
} |
||||||
|
|
||||||
|
function receiveMessage(bytes memory, bytes calldata) |
||||||
|
external |
||||||
|
pure |
||||||
|
returns (bool success) |
||||||
|
{ |
||||||
|
success = true; |
||||||
|
} |
||||||
|
|
||||||
|
function hashSourceAndNonce(uint32 _source, uint256 _nonce) |
||||||
|
public |
||||||
|
pure |
||||||
|
returns (bytes32) |
||||||
|
{ |
||||||
|
return keccak256(abi.encodePacked(_source, _nonce)); |
||||||
|
} |
||||||
|
|
||||||
|
function process( |
||||||
|
bytes32 _nonceId, |
||||||
|
address _recipient, |
||||||
|
uint256 _amount |
||||||
|
) public { |
||||||
|
processedNonces[_nonceId] = true; |
||||||
|
token.mint(_recipient, _amount); |
||||||
|
} |
||||||
|
|
||||||
|
function usedNonces(bytes32 _nonceId) external view returns (bool) { |
||||||
|
return processedNonces[_nonceId]; |
||||||
|
} |
||||||
|
} |
@ -1,46 +0,0 @@ |
|||||||
// SPDX-License-Identifier: Apache-2.0 |
|
||||||
pragma solidity ^0.8.13; |
|
||||||
|
|
||||||
import {ITokenBridgeAdapter} from "../middleware/token-bridge/interfaces/ITokenBridgeAdapter.sol"; |
|
||||||
import {MockToken} from "./MockToken.sol"; |
|
||||||
|
|
||||||
contract MockTokenBridgeAdapter is ITokenBridgeAdapter { |
|
||||||
uint256 public nonce = 0; |
|
||||||
MockToken token; |
|
||||||
|
|
||||||
mapping(uint256 => bool) public isProcessed; |
|
||||||
|
|
||||||
constructor(MockToken _token) { |
|
||||||
token = _token; |
|
||||||
} |
|
||||||
|
|
||||||
function sendTokens( |
|
||||||
uint32, |
|
||||||
bytes32, |
|
||||||
address _token, |
|
||||||
uint256 _amount |
|
||||||
) external override returns (bytes memory _adapterData) { |
|
||||||
require(_token == address(token), "cant bridge this token"); |
|
||||||
token.burn(_amount); |
|
||||||
nonce = nonce + 1; |
|
||||||
return abi.encode(nonce); |
|
||||||
} |
|
||||||
|
|
||||||
function process(uint256 _nonce) public { |
|
||||||
isProcessed[_nonce] = true; |
|
||||||
} |
|
||||||
|
|
||||||
function receiveTokens( |
|
||||||
uint32 _originDomain, // Hyperlane domain |
|
||||||
address _recipientAddress, |
|
||||||
uint256 _amount, |
|
||||||
bytes calldata _adapterData // The adapter data from the message |
|
||||||
) external override returns (address, uint256) { |
|
||||||
_originDomain; |
|
||||||
uint256 _nonce = abi.decode(_adapterData, (uint256)); |
|
||||||
// Check if the transfer was processed first |
|
||||||
require(isProcessed[_nonce], "Transfer has not been processed yet"); |
|
||||||
token.mint(_recipientAddress, _amount); |
|
||||||
return (address(0), 0); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,10 @@ |
|||||||
|
{ |
||||||
|
"goerli": { |
||||||
|
"circleBridgeAdapter": "0xc262a656c99B3a2f1B196dc5BeDa8f4f80D4a878", |
||||||
|
"router": "0x952228cA63f85130534981844050c82b89f373E7" |
||||||
|
}, |
||||||
|
"fuji": { |
||||||
|
"circleBridgeAdapter": "0xc262a656c99B3a2f1B196dc5BeDa8f4f80D4a878", |
||||||
|
"router": "0x952228cA63f85130534981844050c82b89f373E7" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
{ |
||||||
|
"goerli": [ |
||||||
|
{ |
||||||
|
"name": "TokenBridgeRouter", |
||||||
|
"address": "0x952228cA63f85130534981844050c82b89f373E7", |
||||||
|
"isProxy": false, |
||||||
|
"constructorArguments": "" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "CircleBridgeAdapter", |
||||||
|
"address": "0xc262a656c99B3a2f1B196dc5BeDa8f4f80D4a878", |
||||||
|
"isProxy": false, |
||||||
|
"constructorArguments": "" |
||||||
|
} |
||||||
|
], |
||||||
|
"fuji": [ |
||||||
|
{ |
||||||
|
"name": "TokenBridgeRouter", |
||||||
|
"address": "0x952228cA63f85130534981844050c82b89f373E7", |
||||||
|
"isProxy": false, |
||||||
|
"constructorArguments": "" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "CircleBridgeAdapter", |
||||||
|
"address": "0xc262a656c99B3a2f1B196dc5BeDa8f4f80D4a878", |
||||||
|
"isProxy": false, |
||||||
|
"constructorArguments": "" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
@ -1,20 +1,26 @@ |
|||||||
{ |
{ |
||||||
"alfajores": { |
"alfajores": { |
||||||
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE", |
||||||
|
"TestTokenRecipient": "0x36597C9C49F3c5887A86466398480ddB66aD0759" |
||||||
}, |
}, |
||||||
"fuji": { |
"fuji": { |
||||||
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE", |
||||||
|
"TestTokenRecipient": "0x36597C9C49F3c5887A86466398480ddB66aD0759" |
||||||
}, |
}, |
||||||
"mumbai": { |
"mumbai": { |
||||||
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE", |
||||||
|
"TestTokenRecipient": "0x36597C9C49F3c5887A86466398480ddB66aD0759" |
||||||
}, |
}, |
||||||
"bsctestnet": { |
"bsctestnet": { |
||||||
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE", |
||||||
|
"TestTokenRecipient": "0x36597C9C49F3c5887A86466398480ddB66aD0759" |
||||||
}, |
}, |
||||||
"goerli": { |
"goerli": { |
||||||
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE", |
||||||
|
"TestTokenRecipient": "0x36597C9C49F3c5887A86466398480ddB66aD0759" |
||||||
}, |
}, |
||||||
"moonbasealpha": { |
"moonbasealpha": { |
||||||
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE" |
"TestRecipient": "0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE", |
||||||
|
"TestTokenRecipient": "0x36597C9C49F3c5887A86466398480ddB66aD0759" |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,32 @@ |
|||||||
|
import { |
||||||
|
BridgeAdapterType, |
||||||
|
ChainMap, |
||||||
|
Chains, |
||||||
|
CircleBridgeAdapterConfig, |
||||||
|
chainMetadata, |
||||||
|
} from '@hyperlane-xyz/sdk'; |
||||||
|
|
||||||
|
const circleDomainMapping = [ |
||||||
|
{ hyperlaneDomain: chainMetadata[Chains.goerli].id, circleDomain: 0 }, |
||||||
|
{ hyperlaneDomain: chainMetadata[Chains.fuji].id, circleDomain: 1 }, |
||||||
|
]; |
||||||
|
|
||||||
|
export const circleBridgeAdapterConfig: ChainMap< |
||||||
|
any, |
||||||
|
CircleBridgeAdapterConfig |
||||||
|
> = { |
||||||
|
[Chains.goerli]: { |
||||||
|
type: BridgeAdapterType.Circle, |
||||||
|
circleBridgeAddress: '0xdabec94b97f7b5fca28f050cc8eeac2dc9920476', |
||||||
|
messageTransmitterAddress: '0x40a61d3d2afcf5a5d31fcdf269e575fb99dd87f7', |
||||||
|
usdcAddress: '0x07865c6e87b9f70255377e024ace6630c1eaa37f', |
||||||
|
circleDomainMapping, |
||||||
|
}, |
||||||
|
[Chains.fuji]: { |
||||||
|
type: BridgeAdapterType.Circle, |
||||||
|
circleBridgeAddress: '0x0fc1103927af27af808d03135214718bcedbe9ad', |
||||||
|
messageTransmitterAddress: '0x52fffb3ee8fa7838e9858a2d5e454007b9027c3c', |
||||||
|
usdcAddress: '0x5425890298aed601595a70ab815c96711a31bc65', |
||||||
|
circleDomainMapping, |
||||||
|
}, |
||||||
|
}; |
@ -0,0 +1,48 @@ |
|||||||
|
import path from 'path'; |
||||||
|
|
||||||
|
import { |
||||||
|
HyperlaneCore, |
||||||
|
TokenBridgeDeployer, |
||||||
|
objMap, |
||||||
|
tokenBridgeFactories, |
||||||
|
} from '@hyperlane-xyz/sdk'; |
||||||
|
|
||||||
|
import { circleBridgeAdapterConfig } from '../../config/environments/testnet2/token-bridge'; |
||||||
|
import { deployWithArtifacts } from '../../src/deploy'; |
||||||
|
import { getConfiguration } from '../helloworld/utils'; |
||||||
|
import { |
||||||
|
getCoreEnvironmentConfig, |
||||||
|
getEnvironment, |
||||||
|
getEnvironmentDirectory, |
||||||
|
} from '../utils'; |
||||||
|
|
||||||
|
async function main() { |
||||||
|
const environment = await getEnvironment(); |
||||||
|
const coreConfig = getCoreEnvironmentConfig(environment); |
||||||
|
const multiProvider = await coreConfig.getMultiProvider(); |
||||||
|
const core = HyperlaneCore.fromEnvironment(environment, multiProvider as any); |
||||||
|
|
||||||
|
const dir = path.join( |
||||||
|
getEnvironmentDirectory(environment), |
||||||
|
'middleware/token-bridge', |
||||||
|
); |
||||||
|
|
||||||
|
// config gcp deployer key as owner
|
||||||
|
const ownerConfigMap = await getConfiguration(environment, multiProvider); |
||||||
|
|
||||||
|
const deployer = new TokenBridgeDeployer( |
||||||
|
multiProvider, |
||||||
|
objMap(circleBridgeAdapterConfig, (chain, conf) => ({ |
||||||
|
bridgeAdapterConfigs: [conf], |
||||||
|
...ownerConfigMap[chain], |
||||||
|
})), |
||||||
|
core, |
||||||
|
'TokenBridgeDeploy2', |
||||||
|
); |
||||||
|
|
||||||
|
await deployWithArtifacts(dir, tokenBridgeFactories, deployer); |
||||||
|
} |
||||||
|
|
||||||
|
main() |
||||||
|
.then(() => console.info('Deployment complete')) |
||||||
|
.catch(console.error); |
@ -0,0 +1,7 @@ |
|||||||
|
import { HyperlaneApp } from '../../HyperlaneApp'; |
||||||
|
import { TokenBridgeContracts } from '../../middleware'; |
||||||
|
import { ChainName } from '../../types'; |
||||||
|
|
||||||
|
export class TokenBridgeApp< |
||||||
|
Chain extends ChainName = ChainName, |
||||||
|
> extends HyperlaneApp<TokenBridgeContracts, Chain> {} |
@ -0,0 +1,174 @@ |
|||||||
|
import { ethers } from 'ethers'; |
||||||
|
|
||||||
|
import { |
||||||
|
CircleBridgeAdapter, |
||||||
|
CircleBridgeAdapter__factory, |
||||||
|
TokenBridgeRouter, |
||||||
|
TokenBridgeRouter__factory, |
||||||
|
} from '@hyperlane-xyz/core'; |
||||||
|
import { objMap } from '@hyperlane-xyz/sdk/src/utils/objects'; |
||||||
|
|
||||||
|
import { HyperlaneCore } from '../../core/HyperlaneCore'; |
||||||
|
import { |
||||||
|
TokenBridgeContracts, |
||||||
|
TokenBridgeFactories, |
||||||
|
tokenBridgeFactories, |
||||||
|
} from '../../middleware'; |
||||||
|
import { MultiProvider } from '../../providers/MultiProvider'; |
||||||
|
import { ChainMap, ChainName } from '../../types'; |
||||||
|
import { HyperlaneRouterDeployer } from '../router/HyperlaneRouterDeployer'; |
||||||
|
import { RouterConfig } from '../router/types'; |
||||||
|
|
||||||
|
export enum BridgeAdapterType { |
||||||
|
Circle = 'Circle', |
||||||
|
} |
||||||
|
|
||||||
|
export interface CircleBridgeAdapterConfig { |
||||||
|
type: BridgeAdapterType.Circle; |
||||||
|
circleBridgeAddress: string; |
||||||
|
messageTransmitterAddress: string; |
||||||
|
usdcAddress: string; |
||||||
|
circleDomainMapping: { |
||||||
|
hyperlaneDomain: number; |
||||||
|
circleDomain: number; |
||||||
|
}[]; |
||||||
|
} |
||||||
|
|
||||||
|
export type BridgeAdapterConfig = CircleBridgeAdapterConfig; |
||||||
|
|
||||||
|
export type TokenBridgeConfig = RouterConfig & { |
||||||
|
bridgeAdapterConfigs: BridgeAdapterConfig[]; |
||||||
|
}; |
||||||
|
|
||||||
|
export class TokenBridgeDeployer< |
||||||
|
Chain extends ChainName, |
||||||
|
> extends HyperlaneRouterDeployer< |
||||||
|
Chain, |
||||||
|
TokenBridgeConfig, |
||||||
|
TokenBridgeContracts, |
||||||
|
TokenBridgeFactories |
||||||
|
> { |
||||||
|
constructor( |
||||||
|
multiProvider: MultiProvider<Chain>, |
||||||
|
configMap: ChainMap<Chain, TokenBridgeConfig>, |
||||||
|
protected core: HyperlaneCore<Chain>, |
||||||
|
protected create2salt = 'TokenBridgeDeployerSalt', |
||||||
|
) { |
||||||
|
super(multiProvider, configMap, tokenBridgeFactories, {}); |
||||||
|
} |
||||||
|
|
||||||
|
async enrollRemoteRouters( |
||||||
|
contractsMap: ChainMap<Chain, TokenBridgeContracts>, |
||||||
|
): Promise<void> { |
||||||
|
// Enroll the TokenBridgeRouter with each other
|
||||||
|
await super.enrollRemoteRouters(contractsMap); |
||||||
|
|
||||||
|
// Enroll the circle adapters with each other
|
||||||
|
await super.enrollRemoteRouters( |
||||||
|
objMap(contractsMap, (_chain, contracts) => ({ |
||||||
|
router: contracts.circleBridgeAdapter!, |
||||||
|
})), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Custom contract deployment logic can go here
|
||||||
|
// If no custom logic is needed, call deployContract for the router
|
||||||
|
async deployContracts( |
||||||
|
chain: Chain, |
||||||
|
config: TokenBridgeConfig, |
||||||
|
): Promise<TokenBridgeContracts> { |
||||||
|
const initCalldata = |
||||||
|
TokenBridgeRouter__factory.createInterface().encodeFunctionData( |
||||||
|
'initialize', |
||||||
|
[config.owner, config.connectionManager, config.interchainGasPaymaster], |
||||||
|
); |
||||||
|
const router = await this.deployContract(chain, 'router', [], { |
||||||
|
create2Salt: this.create2salt, |
||||||
|
initCalldata, |
||||||
|
}); |
||||||
|
|
||||||
|
const bridgeAdapters: Partial<TokenBridgeContracts> = {}; |
||||||
|
|
||||||
|
for (const adapterConfig of config.bridgeAdapterConfigs) { |
||||||
|
if (adapterConfig.type === BridgeAdapterType.Circle) { |
||||||
|
bridgeAdapters.circleBridgeAdapter = |
||||||
|
await this.deployCircleBridgeAdapter( |
||||||
|
chain, |
||||||
|
adapterConfig, |
||||||
|
config.owner, |
||||||
|
router, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
...bridgeAdapters, |
||||||
|
router, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
async deployCircleBridgeAdapter( |
||||||
|
chain: Chain, |
||||||
|
adapterConfig: CircleBridgeAdapterConfig, |
||||||
|
owner: string, |
||||||
|
router: TokenBridgeRouter, |
||||||
|
): Promise<CircleBridgeAdapter> { |
||||||
|
const cc = this.multiProvider.getChainConnection(chain); |
||||||
|
const initCalldata = |
||||||
|
CircleBridgeAdapter__factory.createInterface().encodeFunctionData( |
||||||
|
'initialize', |
||||||
|
[ |
||||||
|
owner, |
||||||
|
adapterConfig.circleBridgeAddress, |
||||||
|
adapterConfig.messageTransmitterAddress, |
||||||
|
router.address, |
||||||
|
], |
||||||
|
); |
||||||
|
const circleBridgeAdapter = await this.deployContract( |
||||||
|
chain, |
||||||
|
'circleBridgeAdapter', |
||||||
|
[], |
||||||
|
{ |
||||||
|
create2Salt: this.create2salt, |
||||||
|
initCalldata, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
if ( |
||||||
|
(await circleBridgeAdapter.tokenSymbolToAddress('USDC')) === |
||||||
|
ethers.constants.AddressZero |
||||||
|
) { |
||||||
|
this.logger(`Set USDC token contract`); |
||||||
|
await cc.handleTx( |
||||||
|
circleBridgeAdapter.addToken(adapterConfig.usdcAddress, 'USDC'), |
||||||
|
); |
||||||
|
} |
||||||
|
// Set domain mappings
|
||||||
|
for (const { |
||||||
|
circleDomain, |
||||||
|
hyperlaneDomain, |
||||||
|
} of adapterConfig.circleDomainMapping) { |
||||||
|
const expectedCircleDomain = |
||||||
|
await circleBridgeAdapter.hyperlaneDomainToCircleDomain( |
||||||
|
hyperlaneDomain, |
||||||
|
); |
||||||
|
if (expectedCircleDomain === circleDomain) continue; |
||||||
|
|
||||||
|
this.logger( |
||||||
|
`Set circle domain ${circleDomain} for hyperlane domain ${hyperlaneDomain}`, |
||||||
|
); |
||||||
|
await cc.handleTx( |
||||||
|
circleBridgeAdapter.addDomain(hyperlaneDomain, circleDomain), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
this.logger('Set CircleTokenBridgeAdapter on Router'); |
||||||
|
await cc.handleTx( |
||||||
|
router.setTokenBridgeAdapter( |
||||||
|
adapterConfig.type, |
||||||
|
circleBridgeAdapter.address, |
||||||
|
), |
||||||
|
); |
||||||
|
return circleBridgeAdapter; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,138 @@ |
|||||||
|
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; |
||||||
|
import { expect } from 'chai'; |
||||||
|
import { ethers } from 'hardhat'; |
||||||
|
|
||||||
|
import { |
||||||
|
MockCircleBridge, |
||||||
|
MockCircleBridge__factory, |
||||||
|
MockCircleMessageTransmitter, |
||||||
|
MockCircleMessageTransmitter__factory, |
||||||
|
MockToken, |
||||||
|
MockToken__factory, |
||||||
|
TestTokenBridgeMessageRecipient__factory, |
||||||
|
TokenBridgeRouter, |
||||||
|
} from '@hyperlane-xyz/core'; |
||||||
|
import { utils } from '@hyperlane-xyz/utils'; |
||||||
|
|
||||||
|
import { testChainConnectionConfigs } from '../consts/chainConnectionConfigs'; |
||||||
|
import { TestCoreApp } from '../core/TestCoreApp'; |
||||||
|
import { TestCoreDeployer } from '../core/TestCoreDeployer'; |
||||||
|
import { TokenBridgeApp } from '../deploy/middleware/TokenBridgeApp'; |
||||||
|
import { |
||||||
|
BridgeAdapterType, |
||||||
|
CircleBridgeAdapterConfig, |
||||||
|
TokenBridgeConfig, |
||||||
|
TokenBridgeDeployer, |
||||||
|
} from '../deploy/middleware/TokenBridgeRouterDeployer'; |
||||||
|
import { getChainToOwnerMap, getTestMultiProvider } from '../deploy/utils'; |
||||||
|
import { ChainNameToDomainId } from '../domains'; |
||||||
|
import { MultiProvider } from '../providers/MultiProvider'; |
||||||
|
import { ChainMap, TestChainNames } from '../types'; |
||||||
|
import { objMap } from '../utils/objects'; |
||||||
|
|
||||||
|
describe('TokenBridgeRouter', async () => { |
||||||
|
const localChain = 'test1'; |
||||||
|
const remoteChain = 'test2'; |
||||||
|
const localDomain = ChainNameToDomainId[localChain]; |
||||||
|
const remoteDomain = ChainNameToDomainId[remoteChain]; |
||||||
|
|
||||||
|
let signer: SignerWithAddress; |
||||||
|
let local: TokenBridgeRouter; |
||||||
|
let multiProvider: MultiProvider<TestChainNames>; |
||||||
|
let coreApp: TestCoreApp; |
||||||
|
|
||||||
|
let tokenBridgeApp: TokenBridgeApp<TestChainNames>; |
||||||
|
let config: ChainMap<TestChainNames, TokenBridgeConfig>; |
||||||
|
let mockToken: MockToken; |
||||||
|
let circleBridge: MockCircleBridge; |
||||||
|
let messageTransmitter: MockCircleMessageTransmitter; |
||||||
|
|
||||||
|
before(async () => { |
||||||
|
[signer] = await ethers.getSigners(); |
||||||
|
|
||||||
|
multiProvider = getTestMultiProvider(signer); |
||||||
|
|
||||||
|
const coreDeployer = new TestCoreDeployer(multiProvider); |
||||||
|
const coreContractsMaps = await coreDeployer.deploy(); |
||||||
|
coreApp = new TestCoreApp(coreContractsMaps, multiProvider); |
||||||
|
|
||||||
|
const mockTokenF = new MockToken__factory(signer); |
||||||
|
mockToken = await mockTokenF.deploy(); |
||||||
|
const circleBridgeF = new MockCircleBridge__factory(signer); |
||||||
|
circleBridge = await circleBridgeF.deploy(mockToken.address); |
||||||
|
const messageTransmitterF = new MockCircleMessageTransmitter__factory( |
||||||
|
signer, |
||||||
|
); |
||||||
|
messageTransmitter = await messageTransmitterF.deploy(mockToken.address); |
||||||
|
|
||||||
|
config = coreApp.extendWithConnectionClientConfig( |
||||||
|
objMap( |
||||||
|
getChainToOwnerMap(testChainConnectionConfigs, signer.address), |
||||||
|
(_chain, conf) => ({ |
||||||
|
...conf, |
||||||
|
bridgeAdapterConfigs: [ |
||||||
|
{ |
||||||
|
type: BridgeAdapterType.Circle, |
||||||
|
circleBridgeAddress: circleBridge.address, |
||||||
|
messageTransmitterAddress: messageTransmitter.address, |
||||||
|
usdcAddress: mockToken.address, |
||||||
|
circleDomainMapping: [ |
||||||
|
{ |
||||||
|
hyperlaneDomain: localDomain, |
||||||
|
circleDomain: localDomain, |
||||||
|
}, |
||||||
|
{ |
||||||
|
hyperlaneDomain: remoteDomain, |
||||||
|
circleDomain: remoteDomain, |
||||||
|
}, |
||||||
|
], |
||||||
|
} as CircleBridgeAdapterConfig, |
||||||
|
], |
||||||
|
}), |
||||||
|
), |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
beforeEach(async () => { |
||||||
|
const TokenBridge = new TokenBridgeDeployer(multiProvider, config, coreApp); |
||||||
|
const contracts = await TokenBridge.deploy(); |
||||||
|
|
||||||
|
tokenBridgeApp = new TokenBridgeApp(contracts, multiProvider); |
||||||
|
|
||||||
|
local = tokenBridgeApp.getContracts(localChain).router; |
||||||
|
}); |
||||||
|
|
||||||
|
it('can transfer tokens', async () => { |
||||||
|
const recipientF = new TestTokenBridgeMessageRecipient__factory(signer); |
||||||
|
const recipient = await recipientF.deploy(); |
||||||
|
|
||||||
|
const amount = 1000; |
||||||
|
await mockToken.mint(signer.address, amount); |
||||||
|
await mockToken.approve(local.address, amount); |
||||||
|
await local.dispatchWithTokens( |
||||||
|
remoteDomain, |
||||||
|
utils.addressToBytes32(recipient.address), |
||||||
|
'0x00', |
||||||
|
mockToken.address, |
||||||
|
amount, |
||||||
|
BridgeAdapterType.Circle, |
||||||
|
); |
||||||
|
|
||||||
|
const transferNonce = await circleBridge.nextNonce(); |
||||||
|
const nonceId = await messageTransmitter.hashSourceAndNonce( |
||||||
|
localDomain, |
||||||
|
transferNonce, |
||||||
|
); |
||||||
|
|
||||||
|
await messageTransmitter.process( |
||||||
|
nonceId, |
||||||
|
tokenBridgeApp.getContracts(remoteChain).circleBridgeAdapter!.address, |
||||||
|
amount, |
||||||
|
); |
||||||
|
await coreApp.processMessages(); |
||||||
|
|
||||||
|
expect((await mockToken.balanceOf(recipient.address)).toNumber()).to.eql( |
||||||
|
amount, |
||||||
|
); |
||||||
|
}); |
||||||
|
}); |
Loading…
Reference in new issue