Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move struct TokensRecieved{} from Child to Library #34

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[profile.default]
solc_version = "0.8.13"
solc_version = "0.8.20"
src = "src"
out = "out"
libs = ["lib"]
Expand Down
8 changes: 2 additions & 6 deletions src/CCTPAndTokenBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "./interfaces/ITokenBridge.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import "./interfaces/CCTPInterfaces/ITokenMessenger.sol";
import "./interfaces/CCTPInterfaces/IMessageTransmitter.sol";

import "./lib/wormhole.lib.sol";
import "./Utils.sol";
import "./TokenBase.sol";
import "./CCTPBase.sol";
Expand Down Expand Up @@ -156,10 +156,6 @@ abstract contract CCTPAndTokenSender is CCTPAndTokenBase {
);
}

function addressToBytes32CCTP(address addr) private pure returns (bytes32) {
return bytes32(uint256(uint160(addr)));
}

// TokenBridge Sender functions, taken from "./TokenBase.sol"

/**
Expand Down Expand Up @@ -449,7 +445,7 @@ abstract contract CCTPAndTokenReceiver is CCTPAndTokenBase {
// Implement this function to handle in-bound deliveries that include a TokenBridge transfer
function receivePayloadAndTokens(
bytes memory payload,
TokenReceived[] memory receivedTokens,
WormholeLib.TokenReceived[] memory receivedTokens,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
Expand Down
13 changes: 3 additions & 10 deletions src/TokenBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import "./interfaces/IWormholeReceiver.sol";
import "./interfaces/IWormholeRelayer.sol";
import "./interfaces/ITokenBridge.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import {Base} from "./WormholeRelayerSDK.sol";

import {Base} from "./Base.sol";
import "./lib/wormhole.lib.sol";
import "./Utils.sol";

abstract contract TokenBase is Base {
Expand Down Expand Up @@ -156,13 +156,6 @@ abstract contract TokenSender is TokenBase {
}

abstract contract TokenReceiver is TokenBase {
struct TokenReceived {
bytes32 tokenHomeAddress;
uint16 tokenHomeChain;
address tokenAddress; // wrapped address if tokenHomeChain !== this chain, else tokenHomeAddress (in evm address format)
uint256 amount;
uint256 amountNormalized; // if decimals > 8, normalized to 8 decimal places
}

function getDecimals(
address tokenAddress
Expand Down Expand Up @@ -243,7 +236,7 @@ abstract contract TokenReceiver is TokenBase {
// Implement this function to handle in-bound deliveries that include a TokenBridge transfer
function receivePayloadAndTokens(
bytes memory payload,
TokenReceived[] memory receivedTokens,
WormholeLib.TokenReceived[] memory receivedTokens,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
Expand Down
4 changes: 4 additions & 0 deletions src/Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ function fromWormholeFormat(bytes32 whFormatAddress) pure returns (address) {
}
return address(uint160(uint256(whFormatAddress)));
}

function addressToBytes32CCTP(address addr) pure returns (bytes32) {
return toWormholeFormat(addr);
}
12 changes: 12 additions & 0 deletions src/lib/wormhole.lib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pragma solidity ^0.8.13;


library WormholeLib{
struct TokenReceived {
bytes32 tokenHomeAddress;
uint16 tokenHomeChain;
address tokenAddress; // wrapped address if tokenHomeChain !== this chain, else tokenHomeAddress (in evm address format)
uint256 amount;
uint256 amountNormalized; // if decimals > 8, normalized to 8 decimal places
}
}
252 changes: 252 additions & 0 deletions test/CCTPAndTokenBridge_Init.test.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
pragma solidity ^0.8.20;

import "../src/initializable-sdk/WormholeRelayerSDK.sol";
import "../src/initializable-sdk/interfaces/IWormholeReceiver.sol";
import "../src/initializable-sdk/interfaces/IWormholeRelayer.sol";
import "../src/initializable-sdk/interfaces/IERC20.sol";

import "../src/initializable-sdk/testing/WormholeRelayerTest.sol";

import "../src/initializable-sdk/Utils.sol";

contract CCTPAndTokenBridgeToy is CCTPAndTokenSender, CCTPAndTokenReceiver {
uint256 constant GAS_LIMIT = 250_000;

constructor(
)
{
setCCTPDomain(5, 7);
setCCTPDomain(6, 1);
}

function init(
address _wormholeRelayer,
address _tokenBridge,
address _wormhole,
address _circleMessageTransmitter,
address _circleTokenMessenger,
address _USDC
) public {
_initCCTPTokenBase(
_wormholeRelayer,
_tokenBridge,
_wormhole,
_circleMessageTransmitter,
_circleTokenMessenger,
_USDC
);
}

function quoteCrossChainDeposit(
uint16 targetChain
) public view returns (uint256 cost) {
// Cost of delivering token and payload to targetChain
(cost, ) = wormholeRelayer.quoteEVMDeliveryPrice(
targetChain,
0,
GAS_LIMIT
);
}

function sendCrossChainDeposit(
uint16 targetChain,
address recipient,
uint256 amount,
address token
) public payable {
uint256 cost = quoteCrossChainDeposit(targetChain);
require(
msg.value == cost,
"msg.value must be quoteCrossChainDeposit(targetChain)"
);

IERC20(token).transferFrom(msg.sender, address(this), amount);

bytes memory payload = abi.encode(recipient, amount);
sendTokenWithPayloadToEvm(
targetChain,
fromWormholeFormat(registeredSenders[targetChain]), // address (on targetChain) to send token and payload to
payload,
0, // receiver value
GAS_LIMIT,
token,
amount
);
}

function sendCrossChainUSDCDeposit(
uint16 targetChain,
address recipient,
uint256 amount
) public payable {
uint256 cost = quoteCrossChainDeposit(targetChain);
require(
msg.value == cost,
"msg.value must be quoteCrossChainDeposit(targetChain)"
);

IERC20(USDC).transferFrom(msg.sender, address(this), amount);

bytes memory payload = abi.encode(recipient, amount);
sendUSDCWithPayloadToEvm(
targetChain,
fromWormholeFormat(registeredSenders[targetChain]), // address (on targetChain) to send token and payload to
payload,
0, // receiver value
GAS_LIMIT,
amount
);
}

function receivePayloadAndUSDC(
bytes memory payload,
uint256 amount,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 // deliveryHash
)
internal
override
onlyWormholeRelayer
isRegisteredSender(sourceChain, sourceAddress)
{
(address recipient, uint256 expectedAmount) = abi.decode(
payload,
(address, uint256)
);
require(amount == expectedAmount, "amount != payload.expectedAmount");
IERC20(USDC).transfer(recipient, amount);
}

function receivePayloadAndTokens(
bytes memory payload,
TokenReceived[] memory receivedTokens,
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 // deliveryHash
)
internal
override
onlyWormholeRelayer
isRegisteredSender(sourceChain, sourceAddress)
{
require(receivedTokens.length == 1, "Expected 1 token transfers");
address recipient = abi.decode(payload, (address));
IERC20(receivedTokens[0].tokenAddress).transfer(
recipient,
receivedTokens[0].amount
);
}
}

contract WormholeSDKTest is WormholeRelayerBasicTest {
CCTPAndTokenBridgeToy CCTPAndTokenBridgeToySource;
CCTPAndTokenBridgeToy CCTPAndTokenBridgeToyTarget;
ERC20Mock USDCSource;
ERC20Mock USDCTarget;
ERC20Mock public token;

constructor() {
setTestnetForkChains(5, 6);
}

function setUpSource() public override {
USDCSource = ERC20Mock(address(sourceChainInfo.USDC));
mintUSDC(sourceChain, address(this), 5000e18);
CCTPAndTokenBridgeToySource = new CCTPAndTokenBridgeToy();
CCTPAndTokenBridgeToySource.init(
address(relayerSource),
address(tokenBridgeSource),
address(wormholeSource),
address(sourceChainInfo.circleMessageTransmitter),
address(sourceChainInfo.circleTokenMessenger),
address(USDCSource)
);
token = createAndAttestToken(sourceChain);
}

function setUpTarget() public override {
USDCTarget = ERC20Mock(address(targetChainInfo.USDC));
mintUSDC(targetChain, address(this), 5000e18);
CCTPAndTokenBridgeToyTarget = new CCTPAndTokenBridgeToy();
CCTPAndTokenBridgeToyTarget.init(
address(relayerTarget),
address(tokenBridgeTarget),
address(wormholeTarget),
address(targetChainInfo.circleMessageTransmitter),
address(targetChainInfo.circleTokenMessenger),
address(USDCTarget)
);
}

function setUpGeneral() public override {
vm.selectFork(sourceFork);
CCTPAndTokenBridgeToySource.setRegisteredSender(
targetChain,
toWormholeFormat(address(CCTPAndTokenBridgeToyTarget))
);

vm.selectFork(targetFork);
CCTPAndTokenBridgeToyTarget.setRegisteredSender(
sourceChain,
toWormholeFormat(address(CCTPAndTokenBridgeToySource))
);
}

function testSendUSDC() public {
vm.selectFork(sourceFork);

uint256 amount = 100e6;
USDCSource.approve(address(CCTPAndTokenBridgeToySource), amount);

vm.selectFork(targetFork);
address recipient = 0x1234567890123456789012345678901234567890;

vm.selectFork(sourceFork);
uint256 cost = CCTPAndTokenBridgeToySource.quoteCrossChainDeposit(
targetChain
);

vm.recordLogs();
CCTPAndTokenBridgeToySource.sendCrossChainUSDCDeposit{value: cost}(
targetChain,
recipient,
amount
);
performDelivery(true);

vm.selectFork(targetFork);
assertEq(IERC20(USDCTarget).balanceOf(recipient), amount);
}

function testSendToken() public {
vm.selectFork(sourceFork);

uint256 amount = 19e17;
token.approve(address(CCTPAndTokenBridgeToySource), amount);

vm.selectFork(targetFork);
address recipient = 0x1234567890123456789012345678901234567890;

vm.selectFork(sourceFork);
uint256 cost = CCTPAndTokenBridgeToySource.quoteCrossChainDeposit(
targetChain
);

vm.recordLogs();
CCTPAndTokenBridgeToySource.sendCrossChainDeposit{value: cost}(
targetChain,
recipient,
amount,
address(token)
);
performDelivery();

vm.selectFork(targetFork);
address wormholeWrappedToken = tokenBridgeTarget.wrappedAsset(
sourceChain,
toWormholeFormat(address(token))
);
assertEq(IERC20(wormholeWrappedToken).balanceOf(recipient), amount);
}
}