evm: token registration changes (#21)

* evm: add sanity check to token registration process

* evm: fetch target token address from circle TokenMinter contract

* evm: remove token registration process

---------

Co-authored-by: gator-boi <gator-boi@users.noreply.github.com>
This commit is contained in:
Reptile 2023-02-15 16:05:10 -05:00 committed by GitHub
parent 5af729041b
commit 565d4abdfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 103 additions and 972 deletions

View File

@ -70,10 +70,6 @@ function updateWormholeFinality(bytes attestedGovernanceMessage)
function registerEmitterAndDomain(bytes attestedGovernanceMessage)
function registerAcceptedToken(bytes attestedGovernanceMessage)
function registerTargetChainToken(bytes attestedGovernanceMessage)
function upgradeContract(bytes attestedGovernanceMessage)
function verifyGovernanceMessage(bytes attestedGovernanceMessage, uint8 action)

View File

@ -25,9 +25,7 @@ contract CircleIntegrationTest is Test {
uint8 constant GOVERNANCE_UPDATE_WORMHOLE_FINALITY = 1;
uint8 constant GOVERNANCE_REGISTER_EMITTER_AND_DOMAIN = 2;
uint8 constant GOVERNANCE_REGISTER_ACCEPTED_TOKEN = 3;
uint8 constant GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN = 4;
uint8 constant GOVERNANCE_UPGRADE_CONTRACT = 5;
uint8 constant GOVERNANCE_UPGRADE_CONTRACT = 3;
// USDC
IUSDC usdc;
@ -111,20 +109,6 @@ contract CircleIntegrationTest is Test {
foreignUsdc = bytes32(uint256(uint160(vm.envAddress("TESTING_FOREIGN_USDC_TOKEN_ADDRESS"))));
}
function registerToken(address token) public {
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_ACCEPTED_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(bytes12(0), token)
);
// Register and should now be accepted.
circleIntegration.registerAcceptedToken(encodedMessage);
}
function registerContract(uint16 foreignChain, bytes32 foreignEmitter, uint32 domain) public {
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
@ -140,9 +124,6 @@ contract CircleIntegrationTest is Test {
}
function prepareCircleIntegrationTest(uint256 amount) public {
// Register USDC with CircleIntegration
registerToken(address(usdc));
// Set up USDC token for test
if (amount > 0) {
// First mint USDC.
@ -654,293 +635,6 @@ contract CircleIntegrationTest is Test {
}
}
function testCannotRegisterTargetChainTokenInvalidLength(
address sourceToken,
uint16 targetChain,
bytes32 targetToken
) public {
vm.assume(sourceToken != address(0));
vm.assume(targetChain > 0 && targetChain != circleIntegration.chainId());
vm.assume(targetToken != bytes32(0));
// First register source token
registerToken(sourceToken);
// Should not already exist.
assertEq(
circleIntegration.targetAcceptedToken(sourceToken, targetChain),
bytes32(0),
"target token already registered"
);
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(bytes12(0), sourceToken, targetChain, targetToken, "But wait! There's more.")
);
// Now register target token.
vm.expectRevert("invalid governance payload length");
circleIntegration.registerTargetChainToken(encodedMessage);
}
function testCannotRegisterAcceptedTokenInvalidLength(address tokenAddress) public {
vm.assume(tokenAddress != address(0));
// Should not already be accepted.
assertTrue(!circleIntegration.isAcceptedToken(tokenAddress), "token already registered");
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_ACCEPTED_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(bytes12(0), tokenAddress, "But wait! There's more.")
);
// Register and should now be accepted.
vm.expectRevert("invalid governance payload length");
circleIntegration.registerAcceptedToken(encodedMessage);
}
function testCannotRegisterAcceptedTokenZeroAddress() public {
// Should not already be accepted.
address tokenAddress = address(0);
assertTrue(!circleIntegration.isAcceptedToken(tokenAddress), "token already registered");
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_ACCEPTED_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(bytes12(0), tokenAddress)
);
// You shall not pass!
vm.expectRevert("token is zero address");
circleIntegration.registerAcceptedToken(encodedMessage);
}
function testCannotRegisterAcceptedTokenInvalidToken(bytes12 garbage, address tokenAddress) public {
vm.assume(garbage != bytes12(0));
vm.assume(tokenAddress != address(0));
// Should not already be accepted.
assertTrue(!circleIntegration.isAcceptedToken(tokenAddress), "token already registered");
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_ACCEPTED_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(garbage, tokenAddress)
);
// You shall not pass!
vm.expectRevert("invalid address");
circleIntegration.registerAcceptedToken(encodedMessage);
}
function testRegisterAcceptedToken(address tokenAddress) public {
vm.assume(tokenAddress != address(0));
// Should not already be accepted.
assertTrue(!circleIntegration.isAcceptedToken(tokenAddress), "token already registered");
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_ACCEPTED_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(bytes12(0), tokenAddress)
);
// Register and should now be accepted.
circleIntegration.registerAcceptedToken(encodedMessage);
assertTrue(circleIntegration.isAcceptedToken(tokenAddress), "token not registered");
}
function testCannotRegisterTargetChainTokenInvalidSourceToken(
bytes12 garbage,
address sourceToken,
uint16 targetChain,
bytes32 targetToken
) public {
vm.assume(garbage != bytes12(0));
vm.assume(sourceToken != address(0));
vm.assume(targetChain > 0 && targetChain != circleIntegration.chainId());
vm.assume(targetToken != bytes32(0));
// Should not already exist.
assertEq(
circleIntegration.targetAcceptedToken(sourceToken, targetChain),
bytes32(0),
"target token already registered"
);
// First attempt to submit garbage source token
{
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(garbage, sourceToken, targetChain, targetToken)
);
// You shall not pass!
vm.expectRevert("invalid address");
circleIntegration.registerTargetChainToken(encodedMessage);
}
// Now use legitimate-looking ERC20 address
{
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(bytes12(0), sourceToken, targetChain, targetToken)
);
// You shall not pass!
vm.expectRevert("source token not accepted");
circleIntegration.registerTargetChainToken(encodedMessage);
}
}
function testCannotRegisterTargetChainTokenInvalidTargetChain(address sourceToken, bytes32 targetToken) public {
vm.assume(sourceToken != address(0));
vm.assume(targetToken != bytes32(0));
// First register source token
registerToken(sourceToken);
// Cannot register chain ID == 0
{
uint16 targetChain = 0;
// Should not already exist.
assertEq(
circleIntegration.targetAcceptedToken(sourceToken, targetChain),
bytes32(0),
"target token already registered"
);
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(bytes12(0), sourceToken, targetChain, targetToken)
);
// You shall not pass!
vm.expectRevert("invalid target chain");
circleIntegration.registerTargetChainToken(encodedMessage);
}
// Cannot register chain ID == this chain's
{
uint16 targetChain = circleIntegration.chainId();
// Should not already exist.
assertEq(
circleIntegration.targetAcceptedToken(sourceToken, targetChain),
bytes32(0),
"target token already registered"
);
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(bytes12(0), sourceToken, targetChain, targetToken)
);
// You shall not pass!
vm.expectRevert("invalid target chain");
circleIntegration.registerTargetChainToken(encodedMessage);
}
}
function testCannotRegisterTargetChainTokenInvalidTargetToken(address sourceToken, uint16 targetChain) public {
vm.assume(sourceToken != address(0));
vm.assume(targetChain > 0 && targetChain != circleIntegration.chainId());
// First register source token
registerToken(sourceToken);
// Should not already exist.
assertEq(
circleIntegration.targetAcceptedToken(sourceToken, targetChain),
bytes32(0),
"target token already registered"
);
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(
bytes12(0),
sourceToken,
targetChain,
bytes32(0) // targetToken
)
);
// You shall not pass!
vm.expectRevert("target token is zero address");
circleIntegration.registerTargetChainToken(encodedMessage);
}
function testRegisterTargetChainToken(address sourceToken, uint16 targetChain, bytes32 targetToken) public {
vm.assume(sourceToken != address(0));
vm.assume(targetChain > 0 && targetChain != circleIntegration.chainId());
vm.assume(targetToken != bytes32(0));
// First register source token
registerToken(sourceToken);
// Should not already exist.
assertEq(
circleIntegration.targetAcceptedToken(sourceToken, targetChain),
bytes32(0),
"target token already registered"
);
bytes memory encodedMessage = wormholeSimulator.makeSignedGovernanceObservation(
wormholeSimulator.governanceChainId(),
wormholeSimulator.governanceContract(),
GOVERNANCE_MODULE,
GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN,
circleIntegration.chainId(),
abi.encodePacked(bytes12(0), sourceToken, targetChain, targetToken)
);
// Now register target token.
circleIntegration.registerTargetChainToken(encodedMessage);
assertEq(
circleIntegration.targetAcceptedToken(sourceToken, targetChain), targetToken, "target token not registered"
);
}
function testCannotUpgradeContractInvalidImplementation(bytes12 garbage, address newImplementation) public {
vm.assume(garbage != bytes12(0));
vm.assume(newImplementation != address(0) && !circleIntegration.isInitialized(newImplementation));
@ -1086,129 +780,4 @@ contract CircleIntegrationTest is Test {
abi.encodePacked("All your base are belong to us") // payload
);
}
// function testTransferTokensWithPayload(uint256 amount, uint16 targetChain, bytes32 mintRecipient) public {
// vm.assume(amount > 0 && amount <= maxUSDCAmountToMint());
// vm.assume(targetChain > 0 && targetChain != circleIntegration.chainId());
// vm.assume(mintRecipient != bytes32(0));
// registerContract(
// targetChain,
// 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef, // foreignEmitter
// 1 // domain
// );
// prepareCircleIntegrationTest(amount);
// // Register target token
// circleIntegration.registerTargetChainToken(
// address(usdc), // sourceToken
// targetChain,
// foreignUsdc // targetToken
// );
// // Record balance.
// uint256 myBalanceBefore = usdc.balanceOf(address(this));
// assertEq(usdc.balanceOf(address(circleIntegration)), 0, "CircleIntegration has balance");
// bytes memory payload = abi.encodePacked("All your base are belong to us");
// vm.recordLogs();
// // Pass.
// circleIntegration.transferTokensWithPayload(address(usdc), amount, targetChain, mintRecipient, payload);
// // Prepare to check transaction logs for expected events.
// Vm.Log[] memory entries = vm.getRecordedLogs();
// // Circle's MessageSent value
// bytes memory message = circleSimulator.findMessageSentInLogs(entries);
// // Wormhole's LogMessagePublished values
// (uint64 sequence, uint32 batchId, bytes memory wormholePayload, uint8 finality) =
// wormholeSimulator.findLogMessagePublishedInLogs(entries);
// assertEq(sequence, 0, "sequence != expected");
// assertEq(batchId, 0, "batchId != expected");
// assertEq(finality, circleIntegration.wormholeFinality(), "finality != circleIntegration.wormholeFinality()");
// // Deserialize wormhole payload
// CircleIntegrationSimulator.DepositWithPayload memory deposit =
// circleSimulator.decodeDepositWithPayload(wormholePayload);
// assertEq(
// deposit.token,
// circleIntegration.targetAcceptedToken(address(usdc), targetChain),
// "deposit.token != expected"
// );
// assertEq(deposit.amount, amount, "deposit.amount != expected");
// assertEq(deposit.sourceDomain, circleIntegration.localDomain(), "deposit.sourceDomain != expected");
// assertEq(
// deposit.targetDomain,
// circleIntegration.getDomainFromChainId(targetChain),
// "deposit.targetDomain != expected"
// );
// assertEq(deposit.nonce, 112396, "deposit.nonce != expected");
// assertEq(deposit.mintRecipient, mintRecipient, "deposit.mintRecipient != expected");
// assertEq(deposit.payload, payload, "deposit.payload != expected");
// // My balance change should equal the amount transferred.
// assertEq(myBalanceBefore - usdc.balanceOf(address(this)), amount, "mismatch in my balance");
// // CircleIntegration's balance should not reflect having any USDC.
// assertEq(usdc.balanceOf(address(circleIntegration)), 0, "CircleIntegration has new balance");
// }
// function borkedTestRedeemTokensWithPayload(uint16 foreignChain) public {
// vm.assume(foreignChain > 0 && foreignChain != circleIntegration.chainId());
// uint32 foreignDomain = 1;
// // Register foreign CircleIntegration
// registerContract(
// foreignChain,
// bytes32(uint256(uint160(address(circleIntegration)))), // foreignEmitter
// foreignDomain // domain
// );
// uint256 amount = 42069;
// uint64 availableNonce = uint64(vm.envUint("TESTING_LAST_NONCE"));
// ICircleIntegration.RedeemParameters memory redeemParams;
// redeemParams.circleBridgeMessage = abi.encodePacked(
// messageTransmitter.version(),
// foreignDomain,
// circleIntegration.localDomain(),
// availableNonce,
// circleBridge.remoteCircleBridges(foreignDomain),
// bytes32(uint256(uint160(address(circleBridge)))),
// circleIntegration.getRegisteredEmitter(foreignChain), // expected caller
// bytes4(0), // ???
// foreignUsdc,
// bytes32(uint256(uint160(address(this)))), // attester
// amount
// );
// redeemParams.circleAttestation = circleSimulator.attestMessage(redeemParams.circleBridgeMessage);
// IWormhole.VM memory wormholeMessage;
// wormholeMessage.timestamp = uint32(block.timestamp);
// wormholeMessage.nonce = 0;
// wormholeMessage.emitterChainId = foreignChain;
// wormholeMessage.emitterAddress = bytes32(uint256(uint160(address(circleIntegration))));
// wormholeMessage.sequence = 0;
// wormholeMessage.consistencyLevel = 1;
// wormholeMessage.payload = circleSimulator.encodeDepositWithPayload(
// CircleIntegrationStructs.DepositWithPayload({
// token: foreignUsdc,
// amount: amount,
// sourceDomain: foreignDomain,
// targetDomain: circleIntegration.localDomain(),
// nonce: availableNonce,
// fromAddress: bytes32(uint256(uint160(address(this)))),
// mintRecipient: bytes32(uint256(uint160(address(this)))),
// payload: abi.encodePacked("All your base are belong to us")
// })
// );
// redeemParams.encodedWormholeMessage = wormholeSimulator.signDevnetObservation(wormholeMessage);
// circleIntegration.redeemTokensWithPayload(redeemParams);
// }
}

View File

@ -9,13 +9,13 @@ fi
# ethereum goerli testnet
anvil \
-m "myth like bonus scare over problem client lizard pioneer submit female collect" \
--port 8545 \
--port 8546 \
--fork-url $ETH_FORK_RPC > anvil_eth.log &
# avalanche fuji testnet
anvil \
-m "myth like bonus scare over problem client lizard pioneer submit female collect" \
--port 8546 \
--port 8547 \
--fork-url $AVAX_FORK_RPC > anvil_avax.log &
sleep 2
@ -33,19 +33,19 @@ echo "deploy contracts"
RELEASE_WORMHOLE_ADDRESS=$ETH_WORMHOLE_ADDRESS \
RELEASE_CIRCLE_BRIDGE_ADDRESS=$ETH_CIRCLE_BRIDGE_ADDRESS \
forge script $EVM_ROOT/forge-scripts/deploy_contracts.sol \
--rpc-url http://localhost:8545 \
--rpc-url http://localhost:8546 \
--private-key $PRIVATE_KEY \
--broadcast --slow > deploy.out 2>&1
RELEASE_WORMHOLE_ADDRESS=$AVAX_WORMHOLE_ADDRESS \
RELEASE_CIRCLE_BRIDGE_ADDRESS=$AVAX_CIRCLE_BRIDGE_ADDRESS \
forge script $EVM_ROOT/forge-scripts/deploy_contracts.sol \
--rpc-url http://localhost:8546 \
--rpc-url http://localhost:8547 \
--private-key $PRIVATE_KEY \
--broadcast --slow >> deploy.out 2>&1
forge script $EVM_ROOT/forge-scripts/deploy_mock_contracts.sol \
--rpc-url http://localhost:8546 \
--rpc-url http://localhost:8547 \
--private-key $PRIVATE_KEY \
--broadcast --slow >> deploy.out 2>&1

View File

@ -62,14 +62,14 @@ contract CircleIntegration is CircleIntegrationMessages, CircleIntegrationGovern
// Call the circle bridge and `depositForBurnWithCaller`. The `mintRecipient`
// should be the target contract (or wallet) composing on this contract.
(bytes32 targetToken, uint64 nonce, uint256 amountReceived) = _transferTokens(
(uint64 nonce, uint256 amountReceived) = _transferTokens(
transferParams.token, transferParams.amount, transferParams.targetChain, transferParams.mintRecipient
);
// encode DepositWithPayload message
bytes memory encodedMessage = encodeDepositWithPayload(
DepositWithPayload({
token: targetToken,
token: addressToBytes32(transferParams.token),
amount: amountReceived,
sourceDomain: localDomain(),
targetDomain: getDomainFromChainId(transferParams.targetChain),
@ -86,7 +86,7 @@ contract CircleIntegration is CircleIntegrationMessages, CircleIntegrationGovern
function _transferTokens(address token, uint256 amount, uint16 targetChain, bytes32 mintRecipient)
internal
returns (bytes32 targetToken, uint64 nonce, uint256 amountReceived)
returns (uint64 nonce, uint256 amountReceived)
{
// sanity check user input
require(amount > 0, "amount must be > 0");
@ -94,10 +94,6 @@ contract CircleIntegration is CircleIntegrationMessages, CircleIntegrationGovern
require(isAcceptedToken(token), "token not accepted");
require(getRegisteredEmitter(targetChain) != bytes32(0), "target contract not registered");
// confirm that the target token was registered
targetToken = targetAcceptedToken(token, targetChain);
require(targetToken != bytes32(0), "target token not registered");
// take custody of tokens
amountReceived = custodyTokens(token, amount);
@ -166,8 +162,13 @@ contract CircleIntegration is CircleIntegrationMessages, CircleIntegrationGovern
// verify the wormhole message
IWormhole.VM memory verifiedMessage = verifyWormholeRedeemMessage(params.encodedWormholeMessage);
// decode the message payload into the DepositWithPayload struct
// Decode the message payload into the DepositWithPayload struct. Call the Circle TokenMinter
// contract to determine the address of the encoded token on this chain.
depositInfo = decodeDepositWithPayload(verifiedMessage.payload);
depositInfo.token = fetchLocalTokenAddress(depositInfo.sourceDomain, depositInfo.token);
// confirm that circle gave us a valid token address
require(depositInfo.token != bytes32(0), "invalid local token address");
// confirm that the caller is the `mintRecipient` to ensure atomic execution
require(addressToBytes32(msg.sender) == depositInfo.mintRecipient, "caller must be mintRecipient");
@ -227,6 +228,25 @@ contract CircleIntegration is CircleIntegrationMessages, CircleIntegrationGovern
return (sourceDomain == circleSourceDomain && targetDomain == circleTargetDomain && nonce == circleNonce);
}
/**
* @notice Fetches the local token address given an address and domain from
* a different chain.
* @param sourceDomain Circle domain for the sending chain.
* @param sourceToken Address of the token for the sending chain.
* @return Address bytes32 formatted address of the `sourceToken` on this chain.
*/
function fetchLocalTokenAddress(uint32 sourceDomain, bytes32 sourceToken)
public
view
returns (bytes32)
{
return addressToBytes32(
circleTokenMinter().remoteTokensToLocalTokens(
keccak256(abi.encodePacked(sourceDomain, sourceToken))
)
);
}
/**
* @notice Converts type address to bytes32 (left-zero-padded)
* @param address_ Address to convert to bytes32

View File

@ -4,6 +4,7 @@ pragma solidity ^0.8.13;
import {IWormhole} from "wormhole/interfaces/IWormhole.sol";
import {ICircleBridge} from "../interfaces/circle/ICircleBridge.sol";
import {IMessageTransmitter} from "../interfaces/circle/IMessageTransmitter.sol";
import {ITokenMinter} from "../interfaces/circle/ITokenMinter.sol";
import {CircleIntegrationSetters} from "./CircleIntegrationSetters.sol";
@ -57,6 +58,14 @@ contract CircleIntegrationGetters is CircleIntegrationSetters {
return IMessageTransmitter(_state.circleTransmitterAddress);
}
/**
* @notice Circle Token Minter contract interface
* @return ITokenMinter interface
*/
function circleTokenMinter() public view returns (ITokenMinter) {
return ITokenMinter(_state.circleTokenMinterAddress);
}
/**
* @notice Registered Circle Integration contracts on other blockchains
* @param emitterChainId Wormhole chain ID for message sender
@ -68,21 +77,11 @@ contract CircleIntegrationGetters is CircleIntegrationSetters {
/**
* @notice Circle Bridge registered token boolean
* @param token Address of token being checked against `acceptedTokens` state variable
* @param token Address of token being checked against the Circle TokenMinter
* @return AcceptedToken bool
*/
function isAcceptedToken(address token) public view returns (bool) {
return _state.acceptedTokens[token];
}
/**
* @notice Circle Bridge registered token on target blockchains boolean
* @param sourceToken Address of token on this chain
* @param chainId_ Wormhole chain ID of target chain
* @return TargetAcceptedToken bytes32
*/
function targetAcceptedToken(address sourceToken, uint16 chainId_) public view returns (bytes32) {
return _state.targetAcceptedTokens[sourceToken][chainId_];
return circleTokenMinter().burnLimitsPerMessage(token) > 0;
}
/**

View File

@ -39,16 +39,8 @@ contract CircleIntegrationGovernance is CircleIntegrationGetters, ERC1967Upgrade
uint8 constant GOVERNANCE_REGISTER_EMITTER_AND_DOMAIN = 2;
uint256 constant GOVERNANCE_REGISTER_EMITTER_AND_DOMAIN_LENGTH = 73;
// for registering Circle Bridge supported assets on this chain
uint8 constant GOVERNANCE_REGISTER_ACCEPTED_TOKEN = 3;
uint256 constant GOVERNANCE_REGISTER_ACCEPTED_TOKEN_LENGTH = 67;
// for registering Circle Bridge supported assets on other blockchains
uint8 constant GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN = 4;
uint256 constant GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN_LENGTH = 101;
// for upgrading implementation (logic) contracts
uint8 constant GOVERNANCE_UPGRADE_CONTRACT = 5;
uint8 constant GOVERNANCE_UPGRADE_CONTRACT = 3;
uint256 constant GOVERNANCE_UPGRADE_CONTRACT_LENGTH = 67;
/**
@ -115,62 +107,6 @@ contract CircleIntegrationGovernance is CircleIntegrationGetters, ERC1967Upgrade
setDomainToChainId(domain, emitterChainId);
}
/**
* @notice `registerAcceptedToken` registers tokens that can be burned + minted
* via the Circle Bridge.
* @param encodedMessage Attested Wormhole governance message with the following
* relevant fields:
* - Field Bytes Type Index
* - sourceToken 32 bytes32 35
*/
function registerAcceptedToken(bytes memory encodedMessage) public {
bytes memory payload = verifyAndConsumeGovernanceMessage(encodedMessage, GOVERNANCE_REGISTER_ACCEPTED_TOKEN);
require(payload.length == GOVERNANCE_REGISTER_ACCEPTED_TOKEN_LENGTH, "invalid governance payload length");
// registering accepted tokens should only be relevant for this contract's chain ID
require(payload.toUint16(33) == chainId(), "invalid target chain");
// token at byte 35 (32 bytes, but last 20 is the address)
address token = readAddressFromBytes32(payload, 35);
require(token != address(0), "token is zero address");
// update the acceptedTokens mapping
addAcceptedToken(token);
}
/**
* @notice `registerTargetChainToken` registers tokens on foreign chains that can be
* burned + minted via the Circle Bridge.
* @param encodedMessage Attested Wormhole governance message with the following
* relevant fields:
* - Field Bytes Type Index
* - sourceToken 32 bytes32 35
* - targetChainId 2 uint16 67
* - targetToken 32 bytes32 69
*/
function registerTargetChainToken(bytes memory encodedMessage) public {
bytes memory payload = verifyAndConsumeGovernanceMessage(encodedMessage, GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN);
require(payload.length == GOVERNANCE_REGISTER_TARGET_CHAIN_TOKEN_LENGTH, "invalid governance payload length");
// registering target chain tokens should only be relevant for this contract's chain ID
require(payload.toUint16(33) == chainId(), "invalid target chain");
// sourceToken at byte 35 (32 bytes, but last 20 is the address)
address sourceToken = readAddressFromBytes32(payload, 35);
require(isAcceptedToken(sourceToken), "source token not accepted");
// targetChain at byte 67
uint16 targetChain = payload.toUint16(67);
require(targetChain > 0 && targetChain != chainId(), "invalid target chain");
// targetToken at byte 69 (hehe)
bytes32 targetToken = payload.toBytes32(69);
require(targetToken != bytes32(0), "target token is zero address");
// update the targetAcceptedTokens mapping
addTargetAcceptedToken(sourceToken, targetChain, targetToken);
}
/**
* @notice `upgradeContract` upgrades the implementation (logic) contract and
* initializes the new implementation.

View File

@ -28,18 +28,14 @@ contract CircleIntegrationSetters is CircleIntegrationState {
_state.circleTransmitterAddress = circleTransmitterAddress_;
}
function setCircleTokenMinter(address circleTokenMinterAddress_) internal {
_state.circleTokenMinterAddress = circleTokenMinterAddress_;
}
function setEmitter(uint16 chainId_, bytes32 emitter) internal {
_state.registeredEmitters[chainId_] = emitter;
}
function addAcceptedToken(address token) internal {
_state.acceptedTokens[token] = true;
}
function addTargetAcceptedToken(address sourceToken, uint16 chainId, bytes32 targetToken) internal {
_state.targetAcceptedTokens[sourceToken][chainId] = targetToken;
}
function setChainIdToDomain(uint16 chainId_, uint32 domain) internal {
_state.chainIdToDomain[chainId_] = domain;
}

View File

@ -6,6 +6,7 @@ import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IWormhole} from "wormhole/interfaces/IWormhole.sol";
import {ICircleBridge} from "../interfaces/circle/ICircleBridge.sol";
import {IMessageTransmitter} from "../interfaces/circle/IMessageTransmitter.sol";
import {ITokenMinter} from "../interfaces/circle/ITokenMinter.sol";
import {CircleIntegrationSetters} from "./CircleIntegrationSetters.sol";
@ -28,10 +29,18 @@ contract CircleIntegrationSetup is CircleIntegrationSetters, ERC1967Upgrade, Con
setCircleBridge(circleBridgeAddress);
setGovernance(governanceChainId, governanceContract);
IMessageTransmitter messageTransmitter = ICircleBridge(circleBridgeAddress).localMessageTransmitter();
// Cache circle bridge
ICircleBridge circleBridge = ICircleBridge(circleBridgeAddress);
// Circle message transmitter contract
IMessageTransmitter messageTransmitter = circleBridge.localMessageTransmitter();
setCircleTransmitter(address(messageTransmitter));
setLocalDomain(messageTransmitter.localDomain());
// Circle token minter contract
ITokenMinter tokenMinter = circleBridge.localMinter();
setCircleTokenMinter(address(tokenMinter));
setEvmChain(block.chainid);
// set the implementation

View File

@ -30,21 +30,15 @@ contract CircleIntegrationStorage {
/// @dev address of the Circle Message Transmitter on this chain
address circleTransmitterAddress;
/// @dev address of the Circle Token Minter on this chain
address circleTokenMinterAddress;
/// @dev mapping of initialized implementation (logic) contracts
mapping(address => bool) initializedImplementations;
/// @dev Wormhole chain ID to known emitter address mapping
mapping(uint16 => bytes32) registeredEmitters;
/// @dev Circle Bridge accepted token to boolean mapping
mapping(address => bool) acceptedTokens;
/**
* @dev Circle Bridge accepted token to target chain accepted token
* (bytes32 zero-left-padded) mapping.
*/
mapping(address => mapping(uint16 => bytes32)) targetAcceptedTokens;
/// @dev Wormhole chain ID to Circle chain domain mapping
mapping(uint16 => uint32) chainIdToDomain;

View File

@ -40,6 +40,11 @@ interface ICircleIntegration {
external
returns (DepositWithPayload memory depositWithPayload);
function fetchLocalTokenAddress(uint32 sourceDomain, bytes32 sourceToken)
external
view
returns (bytes32);
function encodeDepositWithPayload(DepositWithPayload memory message) external pure returns (bytes memory);
function decodeDepositWithPayload(bytes memory encoded) external pure returns (DepositWithPayload memory message);
@ -68,8 +73,6 @@ interface ICircleIntegration {
function localDomain() external view returns (uint32);
function targetAcceptedToken(address sourceToken, uint16 chainId_) external view returns (bytes32);
function verifyGovernanceMessage(bytes memory encodedMessage, uint8 action)
external
view
@ -82,9 +85,5 @@ interface ICircleIntegration {
function registerEmitterAndDomain(bytes memory encodedMessage) external;
function registerAcceptedToken(bytes memory encodedMessage) external;
function registerTargetChainToken(bytes memory encodedMessage) external;
function upgradeContract(bytes memory encodedMessage) external;
}

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import {IMessageTransmitter} from "./IMessageTransmitter.sol";
import {ITokenMinter} from "./ITokenMinter.sol";
interface ICircleBridge {
/**
@ -63,6 +64,8 @@ interface ICircleBridge {
function localMessageTransmitter() external view returns (IMessageTransmitter);
function localMinter() external view returns (ITokenMinter);
function remoteCircleBridges(uint32 domain) external view returns (bytes32);
// owner only methods

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: UNLICENSED
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
interface IMessageTransmitter {

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
/**
* @title ITokenMinter
* @notice interface for minter of tokens that are mintable, burnable, and interchangeable
* across domains.
*/
interface ITokenMinter {
function burnLimitsPerMessage(address token) external view returns (uint256);
function remoteTokensToLocalTokens(bytes32 sourceIdHash) external view returns (address);
}

View File

@ -1,11 +1,5 @@
import {ethers} from "ethers";
import {
CHAIN_ID_ALGORAND,
CHAIN_ID_AVAX,
CHAIN_ID_ETH,
tryNativeToUint8Array,
ChainId,
} from "@certusone/wormhole-sdk";
import {tryNativeToUint8Array} from "@certusone/wormhole-sdk";
import {MockGuardians} from "@certusone/wormhole-sdk/lib/cjs/mock";
import {CircleGovernanceEmitter} from "../test/helpers/mock";
import {abi as WORMHOLE_ABI} from "../../out/IWormhole.sol/IWormhole.json";
@ -90,44 +84,6 @@ async function registerEmitterAndDomain() {
expect(Buffer.compare(registeredEmitter, emitterAddress)).to.equal(0);
}
async function registerAcceptedToken() {
// MockGuardians and MockCircleAttester objects
const guardians = new MockGuardians(
await wormhole.getCurrentGuardianSetIndex(),
[process.env.TESTNET_GUARDIAN_KEY!]
);
const timestamp = getTimeNow();
const chainId = Number(process.env.SOURCE_CHAIN_ID!);
// create unsigned registerAcceptedToken governance message
const published = governance.publishCircleIntegrationRegisterAcceptedToken(
timestamp,
chainId,
process.env.SOURCE_USDC_ADDRESS!
);
// sign governance message with guardian key
const signedMessage = guardians.addSignatures(published, [0]);
// register the token
const receipt = await circleIntegration
.registerAcceptedToken(signedMessage)
.then((tx: ethers.ContractTransaction) => tx.wait())
.catch((msg: string) => {
// should not happen
console.log(msg);
return null;
});
expect(receipt).is.not.null;
// check contract state to verify the registration
const accepted = await circleIntegration.isAcceptedToken(
process.env.SOURCE_USDC_ADDRESS!
);
expect(accepted).is.true;
}
async function updateFinality() {
// MockGuardians and MockCircleAttester objects
const guardians = new MockGuardians(

View File

@ -1,44 +1,20 @@
import {expect} from "chai";
import {ethers} from "ethers";
import {tryNativeToUint8Array} from "@certusone/wormhole-sdk";
import {
CHAIN_ID_ALGORAND,
CHAIN_ID_AVAX,
CHAIN_ID_ETH,
tryNativeToUint8Array,
} from "@certusone/wormhole-sdk";
import {
AVAX_USDC_TOKEN_ADDRESS,
ETH_USDC_TOKEN_ADDRESS,
GUARDIAN_PRIVATE_KEY,
WORMHOLE_GUARDIAN_SET_INDEX,
ETH_LOCALHOST,
WALLET_PRIVATE_KEY,
WALLET_PRIVATE_KEY_TWO,
AVAX_LOCALHOST,
ETH_FORK_CHAIN_ID,
AVAX_FORK_CHAIN_ID,
ETH_WORMHOLE_ADDRESS,
AVAX_WORMHOLE_ADDRESS,
} from "./helpers/consts";
import {
ICircleIntegration__factory,
IUSDC__factory,
IMockIntegration__factory,
IWormhole__factory,
} from "../src/ethers-contracts";
import {ICircleIntegration__factory} from "../src/ethers-contracts";
import {MockGuardians} from "@certusone/wormhole-sdk/lib/cjs/mock";
import {RedeemParameters, TransferParameters} from "../src";
import {findCircleMessageInLogs} from "../src/logs";
import {CircleGovernanceEmitter} from "./helpers/mock";
import {
getTimeNow,
MockCircleAttester,
readCircleIntegrationProxyAddress,
readMockIntegrationAddress,
findWormholeMessageInLogs,
findRedeemEventInLogs,
} from "./helpers/utils";
import {getTimeNow, readCircleIntegrationProxyAddress} from "./helpers/utils";
describe("Circle Integration Registration", () => {
// ethereum wallet, CircleIntegration contract and USDC contract
@ -48,7 +24,6 @@ describe("Circle Integration Registration", () => {
readCircleIntegrationProxyAddress(ETH_FORK_CHAIN_ID),
ethWallet
);
const ethUsdc = IUSDC__factory.connect(ETH_USDC_TOKEN_ADDRESS, ethWallet);
// avalanche wallet, CircleIntegration contract and USDC contract
const avaxProvider = new ethers.providers.StaticJsonRpcProvider(
@ -59,29 +34,11 @@ describe("Circle Integration Registration", () => {
readCircleIntegrationProxyAddress(AVAX_FORK_CHAIN_ID),
avaxWallet
);
const avaxUsdc = IUSDC__factory.connect(AVAX_USDC_TOKEN_ADDRESS, avaxWallet);
// mock integration contract on avax
const avaxMockIntegration = IMockIntegration__factory.connect(
readMockIntegrationAddress(AVAX_FORK_CHAIN_ID),
avaxWallet
);
// MockGuardians and MockCircleAttester objects
const guardians = new MockGuardians(WORMHOLE_GUARDIAN_SET_INDEX, [
GUARDIAN_PRIVATE_KEY,
]);
const circleAttester = new MockCircleAttester(GUARDIAN_PRIVATE_KEY);
// Wormhole contracts
const ethWormhole = IWormhole__factory.connect(
ETH_WORMHOLE_ADDRESS,
ethWallet
);
const avaxWormhole = IWormhole__factory.connect(
AVAX_WORMHOLE_ADDRESS,
avaxWallet
);
describe("Registrations", () => {
// produces governance VAAs for CircleAttestation contract
@ -127,78 +84,6 @@ describe("Circle Integration Registration", () => {
.then((bytes) => Buffer.from(ethers.utils.arrayify(bytes)));
expect(Buffer.compare(registeredEmitter, emitterAddress)).to.equal(0);
});
it("Should Register Accepted Token", async () => {
const timestamp = getTimeNow();
const chainId = await ethCircleIntegration.chainId();
// create unsigned registerAcceptedToken governance message
const published =
governance.publishCircleIntegrationRegisterAcceptedToken(
timestamp,
chainId,
ETH_USDC_TOKEN_ADDRESS
);
// sign governance message with guardian key
const signedMessage = guardians.addSignatures(published, [0]);
// register the token
const receipt = await ethCircleIntegration
.registerAcceptedToken(signedMessage)
.then((tx) => tx.wait())
.catch((msg) => {
// should not happen
console.log(msg);
return null;
});
expect(receipt).is.not.null;
// check contract state to verify the registration
const accepted = await ethCircleIntegration.isAcceptedToken(
ETH_USDC_TOKEN_ADDRESS
);
expect(accepted).is.true;
});
it("Should Register Target Chain Token", async () => {
const timestamp = getTimeNow();
const chainId = await ethCircleIntegration.chainId();
const targetChain = await avaxCircleIntegration.chainId();
const targetToken = Buffer.from(
tryNativeToUint8Array(AVAX_USDC_TOKEN_ADDRESS, "avalanche")
);
// create unsigned registerTargetChainToken governance message
const published =
governance.publishCircleIntegrationRegisterTargetChainToken(
timestamp,
chainId,
ETH_USDC_TOKEN_ADDRESS,
targetChain,
targetToken
);
// sign governance message with guardian key
const signedMessage = guardians.addSignatures(published, [0]);
// register the target token
const receipt = await ethCircleIntegration
.registerTargetChainToken(signedMessage)
.then((tx) => tx.wait())
.catch((msg) => {
// should not happen
console.log(msg);
return null;
});
expect(receipt).is.not.null;
// check contract state to verify the registration
const registeredTargetToken = await ethCircleIntegration
.targetAcceptedToken(ETH_USDC_TOKEN_ADDRESS, targetChain)
.then((bytes) => Buffer.from(ethers.utils.arrayify(bytes)));
expect(Buffer.compare(registeredTargetToken, targetToken)).to.equal(0);
});
});
describe("Avalanche Fuji Testnet", () => {
@ -239,78 +124,6 @@ describe("Circle Integration Registration", () => {
.then((bytes) => Buffer.from(ethers.utils.arrayify(bytes)));
expect(Buffer.compare(registeredEmitter, emitterAddress)).to.equal(0);
});
it("Should Register Accepted Token", async () => {
const timestamp = getTimeNow();
const chainId = await avaxCircleIntegration.chainId();
// create unsigned registerAcceptedToken governance message
const published =
governance.publishCircleIntegrationRegisterAcceptedToken(
timestamp,
chainId,
AVAX_USDC_TOKEN_ADDRESS
);
// sign governance message with guardian key
const signedMessage = guardians.addSignatures(published, [0]);
// register the token
const receipt = await avaxCircleIntegration
.registerAcceptedToken(signedMessage)
.then((tx) => tx.wait())
.catch((msg) => {
// should not happen
console.log(msg);
return null;
});
expect(receipt).is.not.null;
// check contract state to verify the registration
const accepted = await avaxCircleIntegration.isAcceptedToken(
AVAX_USDC_TOKEN_ADDRESS
);
expect(accepted).is.true;
});
it("Should Register Target Chain Token", async () => {
const timestamp = getTimeNow();
const chainId = await avaxCircleIntegration.chainId();
const targetChain = await ethCircleIntegration.chainId();
const targetToken = Buffer.from(
tryNativeToUint8Array(ETH_USDC_TOKEN_ADDRESS, "avalanche")
);
// create unsigned registerTargetChainToken governance message
const published =
governance.publishCircleIntegrationRegisterTargetChainToken(
timestamp,
chainId,
AVAX_USDC_TOKEN_ADDRESS,
targetChain,
targetToken
);
// sign governance message with guardian key
const signedMessage = guardians.addSignatures(published, [0]);
// register the target token
const receipt = await avaxCircleIntegration
.registerTargetChainToken(signedMessage)
.then((tx) => tx.wait())
.catch((msg) => {
// should not happen
console.log(msg);
return null;
});
expect(receipt).is.not.null;
/// check contract state to verify the registration
const registeredTargetToken = await avaxCircleIntegration
.targetAcceptedToken(AVAX_USDC_TOKEN_ADDRESS, targetChain)
.then((bytes) => Buffer.from(ethers.utils.arrayify(bytes)));
expect(Buffer.compare(registeredTargetToken, targetToken)).to.equal(0);
});
});
});
});

View File

@ -29,10 +29,7 @@ import {
import {MockGuardians} from "@certusone/wormhole-sdk/lib/cjs/mock";
import {RedeemParameters, TransferParameters} from "../src";
import {findCircleMessageInLogs} from "../src/logs";
import {CircleGovernanceEmitter} from "./helpers/mock";
import {
getTimeNow,
MockCircleAttester,
readCircleIntegrationProxyAddress,
readMockIntegrationAddress,
@ -491,74 +488,6 @@ describe("Circle Integration Send and Receive", () => {
expect(failed).is.true;
});
it("Should Not Allow Transfers for Unregistered Target Tokens", async () => {
// initialize governance module
const governance = new CircleGovernanceEmitter();
// store EUROC address
const eurocAddress = "0x53d80871b92dadeD34A4BdFA6838DdFC7f214240";
const timestamp = getTimeNow();
const chainId = await avaxCircleIntegration.chainId();
// publish an unsigned registerAcceptedToken governance message
const published =
governance.publishCircleIntegrationRegisterAcceptedToken(
timestamp,
chainId,
eurocAddress
);
// sign the governance message with the guardian key
const signedMessage = guardians.addSignatures(published, [0]);
// register the token
const receipt = await avaxCircleIntegration
.registerAcceptedToken(signedMessage)
.then((tx) => tx.wait())
.catch((msg) => {
// should not happen
console.log(msg);
return null;
});
expect(receipt).is.not.null;
// check state to confirm the token was registered
const accepted = await avaxCircleIntegration.isAcceptedToken(
eurocAddress
);
expect(accepted).is.true;
// define transferTokensWithPayload function arguments
const params: TransferParameters = {
token: eurocAddress,
amount: amountFromAvax,
targetChain: CHAIN_ID_ALGORAND as number, // unregistered chain
mintRecipient: tryNativeToUint8Array(ethWallet.address, "ethereum"),
};
const batchId = 0; // opt out of batching
const payload = Buffer.from("Sending an unregistered target token :)");
// try to transfer with an unregistered target token
let failed: boolean = false;
try {
const receipt = await avaxCircleIntegration
.transferTokensWithPayload(params, batchId, payload)
.then(async (tx) => {
const receipt = await tx.wait();
return receipt;
});
} catch (e: any) {
expect(
e.error.reason,
"execution reverted: target token not registered"
).to.be.equal;
failed = true;
}
// confirm that the call failed
expect(failed).is.true;
});
it("Should Only Mint Tokens to the Mint Recipient", async () => {
// define transferTokensWithPayload function arguments
const params: TransferParameters = {

View File

@ -1,46 +1,22 @@
import { expect } from "chai";
import { ethers } from "ethers";
import {expect} from "chai";
import {ethers} from "ethers";
import {tryNativeToUint8Array} from "@certusone/wormhole-sdk";
import {
CHAIN_ID_ALGORAND,
CHAIN_ID_AVAX,
CHAIN_ID_ETH,
tryNativeToUint8Array,
} from "@certusone/wormhole-sdk";
import {
AVAX_USDC_TOKEN_ADDRESS,
ETH_USDC_TOKEN_ADDRESS,
GUARDIAN_PRIVATE_KEY,
WORMHOLE_GUARDIAN_SET_INDEX,
ETH_LOCALHOST,
WALLET_PRIVATE_KEY,
WALLET_PRIVATE_KEY_TWO,
AVAX_LOCALHOST,
ETH_FORK_CHAIN_ID,
AVAX_FORK_CHAIN_ID,
ETH_WORMHOLE_ADDRESS,
AVAX_WORMHOLE_ADDRESS,
} from "./helpers/consts";
import {
ICircleIntegration__factory,
IUSDC__factory,
IMockIntegration__factory,
IWormhole__factory,
} from "../src/ethers-contracts";
import { MockGuardians } from "@certusone/wormhole-sdk/lib/cjs/mock";
import { RedeemParameters, TransferParameters } from "../src";
import { findCircleMessageInLogs } from "../src/logs";
import {ICircleIntegration__factory} from "../src/ethers-contracts";
import {MockGuardians} from "@certusone/wormhole-sdk/lib/cjs/mock";
import { CircleGovernanceEmitter } from "./helpers/mock";
import {
getTimeNow,
MockCircleAttester,
readCircleIntegrationProxyAddress,
readMockIntegrationAddress,
findWormholeMessageInLogs,
findRedeemEventInLogs,
} from "./helpers/utils";
import {CircleGovernanceEmitter} from "./helpers/mock";
import {getTimeNow, readCircleIntegrationProxyAddress} from "./helpers/utils";
const { execSync } = require("child_process");
const {execSync} = require("child_process");
describe("Circle Integration Implementation Upgrade", () => {
// ethereum wallet, CircleIntegration contract and USDC contract

View File

@ -1,14 +1,14 @@
import {ethers} from "ethers";
// ethereum goerli testnet fork
export const ETH_LOCALHOST = "http://localhost:8545";
export const ETH_LOCALHOST = "http://localhost:8546";
export const ETH_FORK_CHAIN_ID = Number(process.env.ETH_FORK_CHAIN_ID!);
export const ETH_WORMHOLE_ADDRESS = process.env.ETH_WORMHOLE_ADDRESS!;
export const ETH_USDC_TOKEN_ADDRESS = process.env.ETH_USDC_TOKEN_ADDRESS!;
export const ETH_CIRCLE_BRIDGE_ADDRESS = process.env.ETH_CIRCLE_BRIDGE_ADDRESS!;
// avalanche fuji testnet fork
export const AVAX_LOCALHOST = "http://localhost:8546";
export const AVAX_LOCALHOST = "http://localhost:8547";
export const AVAX_FORK_CHAIN_ID = Number(process.env.AVAX_FORK_CHAIN_ID!);
export const AVAX_WORMHOLE_ADDRESS = process.env.AVAX_WORMHOLE_ADDRESS!;
export const AVAX_USDC_TOKEN_ADDRESS = process.env.AVAX_USDC_TOKEN_ADDRESS!;

View File

@ -1,10 +1,5 @@
import { coalesceChainId, tryNativeToHexString } from "@certusone/wormhole-sdk";
import {
GovernanceEmitter,
MockEmitter,
} from "@certusone/wormhole-sdk/lib/cjs/mock";
import { ethers } from "ethers";
import { DepositWithPayload, ICircleIntegration } from "../../src";
import {GovernanceEmitter} from "@certusone/wormhole-sdk/lib/cjs/mock";
import {ethers} from "ethers";
export interface Transfer {
token: string;
@ -18,34 +13,6 @@ export interface MockDepositWithPayload {
fromAddress: Buffer;
}
export class MockCircleIntegration extends MockEmitter {
domain: number;
foreignCircleIntegration: ICircleIntegration;
constructor(
address: string,
chain: number,
domain: number,
foreignCircleIntegration: ICircleIntegration
) {
super(tryNativeToHexString(address, "ethereum"), chain);
this.domain = domain;
this.foreignCircleIntegration = foreignCircleIntegration;
}
async transferTokensWithPayload() {
// mockParams: MockDepositWithPayload // payload: Buffer, // batchId: number, // transfer: Transfer,
const foreign = this.foreignCircleIntegration;
const targetDomain = await foreign.localDomain();
// const depositWithPayload: DepositWithPayload = {
// }
// const encoded =
// await this.interfaceContract.encodeDepositWithPayload({});
}
}
export class CircleGovernanceEmitter extends GovernanceEmitter {
constructor(startSequence?: number) {
super(
@ -94,50 +61,6 @@ export class CircleGovernanceEmitter extends GovernanceEmitter {
);
}
publishCircleIntegrationRegisterAcceptedToken(
timestamp: number,
chain: number,
tokenAddress: string,
uptickSequence: boolean = true
) {
const payload = Buffer.alloc(32);
payload.write(tryNativeToHexString(tokenAddress, "ethereum"), 0, "hex");
return this.publishGovernanceMessage(
timestamp,
"CircleIntegration",
payload,
3,
chain,
uptickSequence
);
}
publishCircleIntegrationRegisterTargetChainToken(
timestamp: number,
chain: number,
sourceTokenAddress: string,
targetChain: number,
targetTokenAddress: Buffer,
uptickSequence: boolean = true
) {
const payload = Buffer.alloc(66);
payload.write(
tryNativeToHexString(sourceTokenAddress, "ethereum"),
0,
"hex"
);
payload.writeUInt16BE(targetChain, 32);
payload.write(targetTokenAddress.toString("hex"), 34, "hex");
return this.publishGovernanceMessage(
timestamp,
"CircleIntegration",
payload,
4,
chain,
uptickSequence
);
}
publishCircleIntegrationUpgradeContract(
timestamp: number,
chain: number,
@ -149,7 +72,7 @@ export class CircleGovernanceEmitter extends GovernanceEmitter {
timestamp,
"CircleIntegration",
payload,
5,
3,
chain,
uptickSequence
);