contract AtomicTokenProxy is AtomicUtils, AtomicTypes{
    AtomicBlue constant atomic = AtomicBlue(0x0);

    // perform a distributed swap and transfer outcome to _receipient
    function swapAndSend(
        SwapParams calldata _swap,
        
        DistributionParams calldata _distribution,
        
        address payable _receipient
    ) public payable returns (uint _output){
        // deposit tokens to executor
        claimTokenFromSenderTo(_swap.sellToken, _swap.input, address(atomic));
        
        // execute swaps on behalf of sender
        _output = atomic.swapAndSend.value(msg.value)(_swap, _distribution, _receipient);
    }
    
    // perform a multi-path distributed swap and transfer outcome to _receipient
    function multiPathSwapAndSend(
        SwapParams calldata _swap,
        
        Token[] calldata _path,
        
        DistributionParams[] calldata _distribution,
        
        address payable _receipient
    ) public payable returns (uint _output){
        // deposit tokens to executor
        claimTokenFromSenderTo(_swap.sellToken, _swap.input, address(atomic));
        
        // execute swaps on behalf of sender
        _output = atomic.multiPathSwapAndSend.value(msg.value)(
            _swap,
            _path,
            _distribution,
            _receipient
        );
    }
    
    // perform a distributed swap
    function swap(
        SwapParams calldata _swap,
        DistributionParams calldata _distribution
    ) public payable returns (uint _output){
        return swapAndSend(_swap, _distribution, msg.sender);
    }
    
    // perform a distributed swap and burn optimal gastoken amount afterwards
    function swapWithGasTokens(
        SwapParams calldata _swap,
        DistributionParams calldata _distribution,
        IGasToken _gasToken,
        uint _gasQtyPerToken
    ) public payable returns (uint _output){
        uint startGas = gasleft();
        _output = swapAndSend(_swap, _distribution, msg.sender);
        _gasToken.freeFromUpTo(msg.sender, (startGas - gasleft() + 25000) / _gasQtyPerToken);
    }
    
    // perform a multi-path distributed swap
    function multiPathSwap(
        SwapParams calldata _swap,
        Token[] calldata _path,
        DistributionParams[] calldata _distribution
    ) public payable returns (uint _output){
        return multiPathSwapAndSend(_swap, _path, _distribution, msg.sender);
    }
    
    // perform a multi-path distributed swap and burn optimal gastoken amount afterwards
    function multiPathSwapWithGasTokens(
        SwapParams calldata _swap,
        Token[] calldata _path,
        DistributionParams[] calldata _distribution,
        IGasToken _gasToken,
        uint _gasQtyPerToken
    ) public payable returns (uint _output){
        uint startGas = gasleft();
        _output = multiPathSwapAndSend(_swap, _path, _distribution, msg.sender);
        _gasToken.freeFromUpTo(msg.sender, (startGas - gasleft() + 25000) / _gasQtyPerToken);
    }
    
    // perform a distributed swap, send outcome to _receipient and burn optimal gastoken amount afterwards
    function swapAndSendWithGasTokens(
        SwapParams calldata _swap,
        DistributionParams calldata _distribution,
        address payable _receipient,
        IGasToken _gasToken,
        uint _gasQtyPerToken
    ) public payable returns (uint _output){
        uint startGas = gasleft();
        _output = swapAndSend(_swap, _distribution, _receipient);
        _gasToken.freeFromUpTo(msg.sender, (startGas - gasleft() + 25000) / _gasQtyPerToken);
    }
    
    // perform a multi-path distributed swap, send outcome to _receipient and burn optimal gastoken amount afterwards
    function multiPathSwapAndSendWithGasTokens(
        SwapParams calldata _swap,
        Token[] calldata _path,
        DistributionParams[] calldata _distribution,
        address payable _receipient,
        IGasToken _gasToken,
        uint _gasQtyPerToken
    ) public payable returns (uint _output){
        uint startGas = gasleft();
        _output = multiPathSwapAndSend(_swap, _path, _distribution, _receipient);
        _gasToken.freeFromUpTo(msg.sender, (startGas - gasleft() + 25000) / _gasQtyPerToken);
    }
}
function multiPathSwapAndSend(
    SwapParams calldata _swap,
        
    Token[] calldata _path,
        
    DistributionParams[] calldata _distribution,
        
    address payable _receipient
) public payable returns (uint _output){
    // deposit tokens to executor
    claimTokenFromSenderTo(_swap.sellToken, _swap.input, address(atomic));
        
    // execute swaps on behalf of sender
    _output = atomic.multiPathSwapAndSend.value(msg.value)(
        _swap,
        _path,
        _distribution,
        _receipient
    );
}

// perform a multi-path distributed swap and transfer outcome to _receipient