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

feat: interchain transfer everywhere #104

Merged
merged 61 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
fbaded0
renamed folder and changed version
Foivos Jul 6, 2023
8a6d845
npmignore
Foivos Jul 6, 2023
607de5c
npmignore
Foivos Jul 6, 2023
52fc054
Merge remote-tracking branch 'origin/main' into feat/dist
Foivos Jul 6, 2023
f5b5f56
change version
Foivos Jul 6, 2023
4c5738d
using include pattern instead.
Foivos Jul 6, 2023
15e606b
Merge remote-tracking branch 'origin/main' into feat/dist0-1-1
Foivos Jul 6, 2023
d998f1b
Merge branch 'main' into feat/dist0-1-1
Foivos Jul 11, 2023
0548a6e
Fixed most of the things least auhority suggested.
Foivos Jul 11, 2023
8397b07
made lint happy
Foivos Jul 11, 2023
f2e5ea8
Apply suggestions from code review
milapsheth Jul 12, 2023
32ce490
fixed some bugs
Foivos Jul 19, 2023
80616c5
Merge remote-tracking branch 'origin/main' into feat/least-autority-f…
Foivos Jul 19, 2023
dc516fe
added events
Foivos Jul 19, 2023
d6fa384
rename set to transfer for distributor and operator
Foivos Jul 24, 2023
5cc8fbb
changed standardized token to always allow token managers to mint/bur…
Foivos Jul 24, 2023
ea255e0
using immutable storage for remoteAddressValidator address to save gas
Foivos Jul 24, 2023
09bf99a
Merge remote-tracking branch 'origin/main' into feat/gas-optimizations
Foivos Jul 24, 2023
650ca71
Added some recommended changes
Foivos Jul 26, 2023
c69e0a4
added milap's suggested changes
Foivos Jul 26, 2023
63a18e4
Merge remote-tracking branch 'origin/main' into feat/gas-optimizations
Foivos Jul 26, 2023
2993930
Fixed some names and some minor gas optimizations
Foivos Jul 26, 2023
9074ed1
prettier and lint
Foivos Jul 26, 2023
d3cb150
stash
Foivos Jul 27, 2023
53fe6fe
import .env in hardhat.config
Foivos Jul 27, 2023
7eb5207
trying to fix .env.example
Foivos Jul 27, 2023
bf603eb
Added some getters in IRemoteAddressValidator and removed useless che…
Foivos Jul 27, 2023
f1723c3
removed ternary operators
Foivos Jul 27, 2023
bc8ed59
made lint happy
Foivos Jul 27, 2023
7661e1f
Merge branch 'feat/gas-optimizations' into feat/use-create3-lib
Foivos Jul 27, 2023
4bf89db
made lint happy
Foivos Jul 27, 2023
512d126
Added a new token manager to handle fee on transfer and added some te…
Foivos Jul 27, 2023
9e43b1c
fixed the liquidity pool check.
Foivos Jul 28, 2023
ba29a2a
Merge remote-tracking branch 'origin/main' into feat/use-create3-lib
Foivos Jul 28, 2023
1e2aeb3
fix a duplication bug
Foivos Jul 28, 2023
6c62270
lint
Foivos Jul 28, 2023
30c1e53
added some more tests
Foivos Jul 28, 2023
51b70c1
Added more tests
Foivos Jul 28, 2023
6b3f5ed
Merge branch 'feat/use-create3-lib' into feat/fee-on-transfer-separate
Foivos Jul 28, 2023
3e6d85f
Added proper re-entrancy protection for fee on transfer token managers.
Foivos Jul 28, 2023
d4075b8
change to tx.origin for refunds
Foivos Jul 28, 2023
5d9d8ce
Added support for more kinds of addresses.
Foivos Jul 28, 2023
1b35cbf
some minor gas opts
Foivos Jul 28, 2023
412dce9
some more gas optimizations.
Foivos Jul 28, 2023
0a8519f
Added a getter for chain name to the remote address validator.
Foivos Jul 28, 2023
959e495
moved the tokenManager getter functionality to a separate contract wh…
Foivos Jul 28, 2023
0d09703
made lint happy
Foivos Jul 28, 2023
2e44518
Removed tokenManagerGetter and put params into tokenManagers
Foivos Jul 28, 2023
6027ffe
Added separate tokenManager interfaces
Foivos Jul 28, 2023
04041d1
addressed ackeeblockchains's 3.0 report
Foivos Aug 23, 2023
6ee5d06
prettier
Foivos Aug 23, 2023
5acf4a8
added interchain transfer methods to the service and unified receivin…
Foivos Aug 28, 2023
244ff61
made lint happy
Foivos Aug 28, 2023
4e56576
rename sendToken to interchainTransfer
Foivos Aug 29, 2023
a8dc8a4
changed sendToken everywhere
Foivos Aug 29, 2023
5cf5ea5
Merge branch 'main' into feat/move-views-out-of-serivce
re1ro Sep 27, 2023
ab32389
Merge branch 'feat/move-views-out-of-serivce' into feat/ackee-3.0
re1ro Sep 27, 2023
8a21176
Merge branch 'feat/ackee-3.0' into feat/interchain-transsfer-everywhere
re1ro Sep 27, 2023
d980fdd
Merge remote-tracking branch 'origin/main' into feat/interchain-trans…
Foivos Oct 2, 2023
5c06a3b
remove duplictes from merge
Foivos Oct 2, 2023
eb4cc14
Merge remote-tracking branch 'origin/main' into feat/interchain-trans…
Foivos Oct 2, 2023
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
216 changes: 113 additions & 103 deletions contracts/interchain-token-service/InterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -397,57 +397,65 @@ contract InterchainTokenService is

/**
* @notice Uses the caller's tokens to fullfill a sendCall ahead of time. Use this only if you have detected an outgoing
* sendToken that matches the parameters passed here.
* interchainTransfer that matches the parameters passed here.
* @dev This is not to be used with fee on transfer tokens as it will incur losses for the express caller.
* @param tokenId the tokenId of the TokenManager used.
* @param destinationAddress the destinationAddress for the sendToken.
* @param amount the amount of token to give.
* @param payload the payload of the receive token
* @param commandId the sendHash detected at the sourceChain.
*/
function expressReceiveToken(bytes32 tokenId, address destinationAddress, uint256 amount, bytes32 commandId) external {
function expressReceiveToken(bytes calldata payload, bytes32 commandId, string calldata sourceChain) external {
if (gateway.isCommandExecuted(commandId)) revert AlreadyExecuted(commandId);

address caller = msg.sender;
_setExpressReceiveToken(payload, commandId, caller);

(uint256 selector, bytes32 tokenId, bytes memory destinationAddressBytes, uint256 amount) = abi.decode(
payload,
(uint256, bytes32, bytes, uint256)
);
address destinationAddress = destinationAddressBytes.toAddress();

ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
IERC20 token = IERC20(tokenManager.tokenAddress());

SafeTokenTransferFrom.safeTransferFrom(token, caller, destinationAddress, amount);

_setExpressReceiveToken(tokenId, destinationAddress, amount, commandId, caller);
if (selector == SELECTOR_SEND_TOKEN_WITH_DATA) {
(, , , , bytes memory sourceAddress, bytes memory data) = abi.decode(payload, (uint256, bytes32, bytes, uint256, bytes, bytes));
IInterchainTokenExpressExecutable(destinationAddress).executeWithInterchainToken(
sourceChain,
sourceAddress,
data,
tokenId,
amount
);
} else if (selector != SELECTOR_SEND_TOKEN) {
revert InvalidExpressSelector();
}
}

/**
* @notice Uses the caller's tokens to fullfill a callContractWithInterchainToken ahead of time. Use this only if you have
* detected an outgoing sendToken that matches the parameters passed here.
* @dev This is not to be used with fee on transfer tokens as it will incur losses for the express caller and it will pass an incorrect amount to the contract.
* @param tokenId the tokenId of the TokenManager used.
* @param sourceChain the name of the chain where the call came from.
* @param sourceAddress the caller of callContractWithInterchainToken.
* @param destinationAddress the destinationAddress for the sendToken.
* @param amount the amount of token to give.
* @param data the data to be passed to destinationAddress after giving them the tokens specified.
* @param commandId the sendHash detected at the sourceChain.
*/
function expressReceiveTokenWithData(
function interchainTransfer(
bytes32 tokenId,
string memory sourceChain,
bytes memory sourceAddress,
address destinationAddress,
string calldata destinationChain,
bytes calldata destinationAddress,
uint256 amount,
bytes calldata data,
bytes32 commandId
bytes calldata metadata
) external {
if (gateway.isCommandExecuted(commandId)) revert AlreadyExecuted(commandId);

address caller = msg.sender;
ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
IERC20 token = IERC20(tokenManager.tokenAddress());

SafeTokenTransferFrom.safeTransferFrom(token, caller, destinationAddress, amount);

_setExpressReceiveTokenWithData(tokenId, sourceChain, sourceAddress, destinationAddress, amount, data, commandId, caller);
ITokenManager tokenManager = ITokenManager(getTokenManagerAddress(tokenId));
amount = tokenManager.takeToken(msg.sender, amount);
_transmitSendToken(tokenId, msg.sender, destinationChain, destinationAddress, amount, metadata);
}

_expressExecuteWithInterchainTokenToken(tokenId, destinationAddress, sourceChain, sourceAddress, data, amount);
function sendTokenWithData(
bytes32 tokenId,
string calldata destinationChain,
bytes calldata destinationAddress,
uint256 amount,
bytes calldata data
) external {
ITokenManager tokenManager = ITokenManager(getTokenManagerAddress(tokenId));
amount = tokenManager.takeToken(msg.sender, amount);
uint32 prefix = 0;
_transmitSendToken(tokenId, msg.sender, destinationChain, destinationAddress, amount, abi.encodePacked(prefix, data));
}

/*********************\
Expand All @@ -459,7 +467,7 @@ contract InterchainTokenService is
* @param tokenId the tokenId of the TokenManager (which must be the msg.sender).
* @param sourceAddress the address where the token is coming from, which will also be used for reimbursement of gas.
* @param destinationChain the name of the chain to send tokens to.
* @param destinationAddress the destinationAddress for the sendToken.
* @param destinationAddress the destinationAddress for the interchainTransfer.
* @param amount the amount of token to give.
* @param metadata the data to be passed to the destination.
*/
Expand All @@ -471,19 +479,7 @@ contract InterchainTokenService is
uint256 amount,
bytes calldata metadata
) external payable onlyTokenManager(tokenId) notPaused {
bytes memory payload;
if (metadata.length < 4) {
payload = abi.encode(SELECTOR_SEND_TOKEN, tokenId, destinationAddress, amount);
_callContract(destinationChain, payload, msg.value);
emit TokenSent(tokenId, destinationChain, destinationAddress, amount);
return;
}
uint32 version;
(version, metadata) = _decodeMetadata(metadata);
if (version > 0) revert InvalidMetadataVersion(version);
payload = abi.encode(SELECTOR_SEND_TOKEN_WITH_DATA, tokenId, destinationAddress, amount, sourceAddress.toBytes(), metadata);
_callContract(destinationChain, payload, msg.value);
emit TokenSentWithData(tokenId, destinationChain, destinationAddress, amount, sourceAddress, metadata);
_transmitSendToken(tokenId, sourceAddress, destinationChain, destinationAddress, amount, metadata);
}

/*************\
Expand Down Expand Up @@ -541,10 +537,8 @@ contract InterchainTokenService is
bytes calldata payload
) internal override onlyRemoteService(sourceChain, sourceAddress) notPaused {
uint256 selector = abi.decode(payload, (uint256));
if (selector == SELECTOR_SEND_TOKEN) {
_processSendTokenPayload(sourceChain, payload);
} else if (selector == SELECTOR_SEND_TOKEN_WITH_DATA) {
_processSendTokenWithDataPayload(sourceChain, payload);
if (selector == SELECTOR_SEND_TOKEN || selector == SELECTOR_SEND_TOKEN_WITH_DATA) {
_processSendTokenPayload(sourceChain, payload, selector);
} else if (selector == SELECTOR_DEPLOY_TOKEN_MANAGER) {
_processDeployTokenManagerPayload(payload);
} else if (selector == SELECTOR_DEPLOY_AND_REGISTER_STANDARDIZED_TOKEN) {
Expand All @@ -559,67 +553,45 @@ contract InterchainTokenService is
* @param sourceChain The chain where the transaction originates from
* @param payload The encoded data payload to be processed
*/
function _processSendTokenPayload(string calldata sourceChain, bytes calldata payload) internal {
(, bytes32 tokenId, bytes memory destinationAddressBytes, uint256 amount) = abi.decode(payload, (uint256, bytes32, bytes, uint256));
bytes32 commandId;

assembly {
commandId := calldataload(4)
}
address destinationAddress = destinationAddressBytes.toAddress();
ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
address expressCaller = _popExpressReceiveToken(tokenId, destinationAddress, amount, commandId);
if (expressCaller == address(0)) {
amount = tokenManager.giveToken(destinationAddress, amount);
emit TokenReceived(tokenId, sourceChain, destinationAddress, amount);
} else {
amount = tokenManager.giveToken(expressCaller, amount);
}
}

/**
* @notice Processes a send token with data payload.
* @param sourceChain The chain where the transaction originates from
* @param payload The encoded data payload to be processed
*/
function _processSendTokenWithDataPayload(string calldata sourceChain, bytes calldata payload) internal {
function _processSendTokenPayload(string calldata sourceChain, bytes calldata payload, uint256 selector) internal {
bytes32 tokenId;
uint256 amount;
bytes memory sourceAddress;
bytes memory data;
address destinationAddress;
uint256 amount;
{
bytes memory destinationAddressBytes;
(, tokenId, destinationAddressBytes, amount) = abi.decode(payload, (uint256, bytes32, bytes, uint256));
destinationAddress = destinationAddressBytes.toAddress();
}
bytes32 commandId;

assembly {
commandId := calldataload(4)
}
ITokenManager tokenManager = ITokenManager(getValidTokenManagerAddress(tokenId));
{
bytes memory destinationAddressBytes;
(, tokenId, destinationAddressBytes, amount, sourceAddress, data) = abi.decode(
payload,
(uint256, bytes32, bytes, uint256, bytes, bytes)
);
destinationAddress = destinationAddressBytes.toAddress();
}
ITokenManager tokenManager = ITokenManager(getTokenManagerAddress(tokenId));
{
address expressCaller = _popExpressReceiveTokenWithData(
tokenId,
sourceChain,
sourceAddress,
destinationAddress,
amount,
data,
commandId
);
address expressCaller = _popExpressReceiveToken(payload, commandId);
if (expressCaller != address(0)) {
amount = tokenManager.giveToken(expressCaller, amount);
return;
}
}
amount = tokenManager.giveToken(destinationAddress, amount);
IInterchainTokenExpressExecutable(destinationAddress).executeWithInterchainToken(sourceChain, sourceAddress, data, tokenId, amount);
emit TokenReceivedWithData(tokenId, sourceChain, destinationAddress, amount, sourceAddress, data);
if (selector == SELECTOR_SEND_TOKEN_WITH_DATA) {
bytes memory sourceAddress;
bytes memory data;
(, , , , sourceAddress, data) = abi.decode(payload, (uint256, bytes32, bytes, uint256, bytes, bytes));

IInterchainTokenExpressExecutable(destinationAddress).executeWithInterchainToken(
sourceChain,
sourceAddress,
data,
tokenId,
amount
);
emit TokenReceivedWithData(tokenId, sourceChain, destinationAddress, amount, sourceAddress, data);
} else {
emit TokenReceived(tokenId, sourceChain, destinationAddress, amount);
}
}

/**
Expand Down Expand Up @@ -839,11 +811,17 @@ contract InterchainTokenService is
emit StandardizedTokenDeployed(tokenId, distributor, name, symbol, decimals, mintAmount, mintTo);
}

function _decodeMetadata(bytes calldata metadata) internal pure returns (uint32 version, bytes calldata data) {
function _decodeMetadata(bytes memory metadata) internal pure returns (uint32 version, bytes memory data) {
data = new bytes(metadata.length - 4);
assembly {
data.length := sub(metadata.length, 4)
data.offset := add(metadata.offset, 4)
version := calldataload(sub(metadata.offset, 28))
version := shr(224, mload(data))
}
if (data.length == 0) return (version, data);
uint256 n = (data.length - 1) / 32;
for (uint256 i = 0; i <= n; ++i) {
assembly {
mstore(add(data, add(32, mul(32, i))), mload(add(metadata, add(36, mul(32, i)))))
}
}
}

Expand All @@ -863,4 +841,36 @@ contract InterchainTokenService is
amount
);
}

/**
* @notice Transmit a sendTokenWithData for the given tokenId. Only callable by a token manager.
* @param tokenId the tokenId of the TokenManager (which must be the msg.sender).
* @param sourceAddress the address where the token is coming from, which will also be used for reimburment of gas.
* @param destinationChain the name of the chain to send tokens to.
* @param destinationAddress the destinationAddress for the interchainTransfer.
* @param amount the amount of token to give.
* @param metadata the data to be passed to the destiantion.
*/
function _transmitSendToken(
bytes32 tokenId,
address sourceAddress,
string calldata destinationChain,
bytes memory destinationAddress,
uint256 amount,
bytes memory metadata
) internal {
bytes memory payload;
if (metadata.length < 4) {
payload = abi.encode(SELECTOR_SEND_TOKEN, tokenId, destinationAddress, amount);
_callContract(destinationChain, payload, msg.value);
emit TokenSent(tokenId, destinationChain, destinationAddress, amount);
return;
}
uint32 version;
(version, metadata) = _decodeMetadata(metadata);
if (version > 0) revert InvalidMetadataVersion(version);
payload = abi.encode(SELECTOR_SEND_TOKEN_WITH_DATA, tokenId, destinationAddress, amount, sourceAddress.toBytes(), metadata);
_callContract(destinationChain, payload, msg.value);
emit TokenSentWithData(tokenId, destinationChain, destinationAddress, amount, sourceAddress, metadata);
}
}
69 changes: 4 additions & 65 deletions contracts/interfaces/IExpressCallHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,75 +6,14 @@ interface IExpressCallHandler {
error AlreadyExpressCalled();
error SameDestinationAsCaller();

event ExpressReceive(
bytes32 indexed tokenId,
address indexed destinationAddress,
uint256 amount,
bytes32 indexed sendHash,
address expressCaller
);
event ExpressExecutionFulfilled(
bytes32 indexed tokenId,
address indexed destinationAddress,
uint256 amount,
bytes32 indexed sendHash,
address expressCaller
);

event ExpressReceiveWithData(
bytes32 indexed tokenId,
string sourceChain,
bytes sourceAddress,
address indexed destinationAddress,
uint256 amount,
bytes data,
bytes32 indexed sendHash,
address expressCaller
);
event ExpressExecutionWithDataFulfilled(
bytes32 indexed tokenId,
string sourceChain,
bytes sourceAddress,
address indexed destinationAddress,
uint256 amount,
bytes data,
bytes32 indexed sendHash,
address expressCaller
);
event ExpressReceive(bytes payload, bytes32 indexed sendHash, address indexed expressCaller);
event ExpressExecutionFulfilled(bytes payload, bytes32 indexed sendHash, address indexed expressCaller);

/**
* @notice Gets the address of the express caller for a specific token transfer
* @param tokenId The ID of the token being sent
* @param destinationAddress The address of the recipient
* @param amount The amount of tokens to be sent
* @param commandId The unique hash for this token transfer
* @return expressCaller The address of the express caller for this token transfer
*/
function getExpressReceiveToken(
bytes32 tokenId,
address destinationAddress,
uint256 amount,
bytes32 commandId
) external view returns (address expressCaller);

/**
* @notice Gets the address of the express caller for a specific token transfer with data
* @param tokenId The ID of the token being sent
* @param sourceChain The chain from which the token will be sent
* @param sourceAddress The originating address of the token on the source chain
* @param destinationAddress The address of the recipient on the destination chain
* @param amount The amount of tokens to be sent
* @param data The data associated with the token transfer
* @param payload the payload for the receive token
* @param commandId The unique hash for this token transfer
* @return expressCaller The address of the express caller for this token transfer
*/
function getExpressReceiveTokenWithData(
bytes32 tokenId,
string memory sourceChain,
bytes memory sourceAddress,
address destinationAddress,
uint256 amount,
bytes calldata data,
bytes32 commandId
) external view returns (address expressCaller);
function getExpressReceiveToken(bytes calldata payload, bytes32 commandId) external view returns (address expressCaller);
}
Loading
Loading