StargateComposed.sol

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()
function swapNativeForNative(
    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 asset
address 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, _toAddr
function sgReceive(
    uint16 _chainId, 
    bytes memory _srcAddress, 
    uint _nonce, 
    address _token, 
    uint amountLD, 
    bytes memory 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.

(address _tokenOut, uint _deadline, uint _amountOutMin, address _toAddr) = abi.decode(payload, (address, uint, uint, address));

IERC20(_token).approve(address(ammRouter), amountLD);

uint _toBalancePreTransferOut = address(_toAddr).balance;

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 = new address[](2);
    path[0] = _token;
    path[1] = IUniswapV2Router02(ammRouter).WETH();

    try IUniswapV2Router02(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
    ) {
        emit ReceivedOnDestination(
            OUT_TO_NATIVE, 
            address(_toAddr).balance.sub(_toBalancePreTransferOut)
        );
    } catch {
        IERC20(_token).transfer(_toAddr, amountLD);
        emit ReceivedOnDestination(_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 = new address[](2);
    path[0] = _token;
    path[1] = _tokenOut;
    try IUniswapV2Router02(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
    ) {
        emit ReceivedOnDestination(_tokenOut, IERC20(_tokenOut).balanceOf(_toAddr).sub(_toAddrTokenBalancePre));
    } catch {
        IERC20(_token).transfer(_toAddr, amountLD);
        emit ReceivedOnDestination(_token, amountLD);
    }
}

Last updated