Leverage Stargate to perform additional smart contract logic on the destination chain!
To test Stargate Composability you can mint yourself testnet tokens using the addresses found on the Test Faucet page.
This contract is an example of the power of composability. A contract on the source chain composes an AMM and Stargate, and a destination contract which has access to an AMM implements sgReceive() receives the tokens and a payload to perform additional logic.
swapNativeForNative()
The StargateComposed.sol contract can swap native on the source chain for native on the destination chain. To swap tokens to another chain using Stargate this contract must use the StargateComposer.sol to interact with Stargate. This code snippet shows how the contract uses it to swap native on the source chain for native on the destination chain.
Warning: When composing, Do Not swap() real funds to a contract address that does not implement sgReceive() or your *will* lose those funds.
/// @param dstChainId The message ordering nonce/// @param bridgeToken The remote Bridge address/// @param srcPoolId The token contract on the local chain/// @param dstPoolId The qty of local _token contract tokens /// @param nativeAmountIn The amount of native token coming in on source /// @param to The address to send the destination tokens to/// @param amountOutMin The minimum amount of stargatePoolId token to get out of amm router/// @param amountOutMinSg The minimum amount of stargatePoolId token to get out on destination chain/// @param amountOutMinDest The minimum amount of native token to receive on destination/// @param deadline The overall deadline/// @param destStargateComposed The destination contract address that must implement sgReceive()functionswapNativeForNative(uint16 dstChainId,address bridgeToken,uint16 srcPoolId,uint16 dstPoolId,uint nativeAmountIn,address to,uint amountOutMin,uint amountOutMinSg,uint amountOutMinDest,uint deadline,address destStargateComposed )
Use the AMM Router
Using the amm router, swap native into the Stargate pool token, sending the output token to this contract.
// special token value that indicates the sgReceive() should swap OUT native assetaddress public OUT_TO_NATIVE=0x0000000000000000000000000000000000000000;uint bridgeAmount;// using the amm router, swap native into the Stargate pool token, sending the output token to this contract{// create path[] for amm swap address[] memory path =new address[](2); path[0] =IUniswapV2Router02(ammRouter).WETH(); // native IN requires that we specify the WETH in path[0] path[1] = bridgeToken; // the bridge token, uint[] memory amounts =IUniswapV2Router02(ammRouter).swapExactETHForTokens{value:nativeAmountIn}( amountOutMin, path,address(this), deadline ); bridgeAmount = amounts[1];require(bridgeAmount >0,'error: ammRouter gave us 0 tokens to swap() with stargate');// this contract needs to approve the stargateComposer to spend its path[1] token!IERC20(bridgeToken).approve(address(stargateComposer), bridgeAmount);}// encode payload data to send to destination contract, which it will handle with sgReceive()bytes memory data;{ data =abi.encode(OUT_TO_NATIVE, deadline, amountOutMinDest, to);}
StargateComposer.swap()
Call StargateComposer swap() to send the tokens to the destination chain.
IStargateRouter(stargateComposer).swap{value:msg.value.sub(nativeAmountIn)}(
dstChainId, // the destination chain id
srcPoolId, // the source Stargate poolId
dstPoolId, // the destination Stargate poolId
payable(msg.sender), // refund adddress. if msg.sender pays too much gas, return extra eth
bridgeAmount, // total tokens to send to destination chain
amountOutMinSg, // minimum
IStargateRouter.lzTxObj(500000, 0, "0x"), // 500,000 for the sgReceive()
abi.encodePacked(destStargateComposed), // destination address, the sgReceive() implementer
data // bytes payload
);
sgReceive()
StargateComposed.sol implements IStargateReceiver so it can implement the sgReceive function to receive the tokens and payload.
/// @param _chainId The remote chainId sending the tokens/// @param _srcAddress The address of the msg.sender who initiated the swap/// @param _nonce The message ordering nonce/// @param _token The token contract on the local chain/// @param amountLD The qty of local _token contract tokens /// @param _payload The bytes containing the _tokenOut, _deadline, _amountOutMin, _toAddrfunctionsgReceive(uint16_chainId,bytesmemory_srcAddress,uint_nonce,address_token,uint amountLD,bytesmemory payload)
Unpack payload, approve, get pre balance of toAddress
Unpack the payload to get _tokenOut,_deadline,_amountOutMin,_toAddr. Approve the amm router so it can swap our tokens. Get the pre balance of the _toAddr to emit an event of exact amount sent.
If _tokenOut is the Zero Address they will get native token
Use the amm router to swap the incoming bridge token into native token
if(_tokenOut ==address(0x0)){address[] memory path =newaddress[](2); path[0] = _token; path[1] =IUniswapV2Router02(ammRouter).WETH();tryIUniswapV2Router02(ammRouter).swapExactTokensForETH( amountLD,// the stable received from stargate at the destination _amountOutMin,// slippage param, min amount native token out path,// path[0]: stabletoken address, path[1]: WETH from sushi router _toAddr,// the address to send the *out* native to _deadline // the unix timestamp deadline ) {emitReceivedOnDestination( OUT_TO_NATIVE,address(_toAddr).balance.sub(_toBalancePreTransferOut) ); } catch {IERC20(_token).transfer(_toAddr, amountLD);emitReceivedOnDestination(_token, amountLD); }}
Else they will get ERC20 token
Use the amm router to swap the incoming bridge token into an ERC20 token
else {uint _toAddrTokenBalancePre =IERC20(_tokenOut).balanceOf(_toAddr);address[] memory path =newaddress[](2); path[0] = _token; path[1] = _tokenOut;tryIUniswapV2Router02(ammRouter).swapExactTokensForTokens( amountLD,// the stable received from stargate at the destination _amountOutMin,// slippage param, min amount native token out path,// path[0]: stabletoken address, path[1]: WETH from sushi router _toAddr,// the address to send the *out* tokens to _deadline // the unix timestamp deadline ) {emitReceivedOnDestination(_tokenOut,IERC20(_tokenOut).balanceOf(_toAddr).sub(_toAddrTokenBalancePre)); } catch {IERC20(_token).transfer(_toAddr, amountLD);emitReceivedOnDestination(_token, amountLD); }}