diff --git a/README.md b/README.md index fd3571fa..094dfa8e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -![Skip Swap](skip_swirl.png "Skipping and Swapping") +![Skip Swap Swirl](assets/skip_swirl.png "Skipping, Swapping, and Swirling") # Skip API Contracts -The Skip Swap contracts in this repository are used in [Skip API](https://api-swagger.skip.money/) to enable any-to-any swaps as part of multi-chain workflows. +The contracts in this repository are used in [Skip API](https://api-swagger.skip.money/) to enable any-to-any swaps as part of multi-chain workflows. Skip API is a unified REST API + SDK that helps developers create more seamless cross-chain experiences for their end users with IBC [(Inter-Blockchain Communication protocol)](https://ibcprotocol.dev/). @@ -15,31 +15,46 @@ The on-chain components of the swapping functionality consist of: 2. Chain/dex-specific swap adapter contracts 3. Chain-specific IBC transfer adapter contracts - ## Entry Point Contract The entry point contract is responsible for providing a standardized interface (w/ safety checks) to interact with Skip Swap across all CosmWasm-enabled chains. The contract: -1. Performs basic validation on the call data -2. Dispatches the swaps provided in the call data to the relevant swap adapter contracts -3. Verifies the amount out received from the swaps is greater than the minimum amount required by the caller after all fees have been subtracted (swap, ibc, affiliate) -4. Dispatches one of the following post-swap actions with the received funds from the swap: - - Transfer to an address on the same chain - - IBC transfer to an address on a different chain (which allows for multi-hop IBC transfers or contract calls if the destination chains support it) - - Call a contract on the same chain +1. Performs basic validation on the call data. +2. If a fee swap is provided, queries the swap adapter contract to determine how much of the coin sent with the contract call is needed to receive the required fee coin(s), and dispatches the swap. +3. Dispatches the user swap provided in the call data to the relevant swap adapter contract. +4. Verifies the amount out received from the swap(s) is greater than the minimum amount required by the caller after all fees have been subtracted (swap, ibc, affiliate). +5. Dispatches one of the following post-swap actions with the received funds from the swap: + - Transfer to an address on the same chain. + - IBC transfer to an address on a different chain (which allows for multi-hop IBC transfers or contract calls if the destination chains support it). + - Call a contract on the same chain. ## Swap Adapter Contracts Swap Adapter contracts are developed and deployed for each swap venue supported by Skip Swap. The contracts are responsible for: -1. Taking the standardized Skip Swap entry point message format and converting it to the specific swap venue's format -2. Swapping by calling the swap venue's respective smart contract or module -3. Providing query methods that can be called by the entry point contract (generally, to any external actor) to simulate multi-hop swaps that either specify an exact amount in (estimating how much would be received from the swap) or an exact amount out (estimating how much is required to get the specified amount out) +1. Taking the standardized entry point swap operations message format and converting it to the specific swap venue's format. +2. Swapping by calling the swap venue's respective smart contract or module. +3. Providing query methods that can be called by the entry point contract (generally, to any external actor) to simulate multi-hop swaps that either specify an exact amount in (estimating how much would be received from the swap) or an exact amount out (estimating how much is required to get the specified amount out). ## IBC Transfer Adapter Contracts IBC Transfer adapter contracts are developed and deployed for each chain supported by Skip Swap. The contracts are responsible for: -1. Dispatching the IBC transfer (with the appropriate IBC fees if required) -2. Failing the entire transaction if the IBC transfer errors on the swap chain (sending the caller back their original funds) -3. Refunding the caller on the swap chain if the IBC transfer errors or times out once it reaches the destination chain (also refunding unused IBC fees) +1. Dispatching the IBC transfer (with the appropriate IBC fees if required). +2. Failing the entire transaction if the IBC transfer errors on the swap chain (sending the caller back their original funds). +3. Refunding the caller on the swap chain if the IBC transfer errors or times out once it reaches the destination chain (also refunding unused IBC fees). + +# Example Flow + +![Skip Swap Flow](assets/skip_swap_flow.png "Skipping, Swapping, and Flowing") + +A simplified example flow showcasing the interactions between the contracts is as follows: +1. A user calls `swap_and_action` on the entry point contract. +2. The entry point contract performs pre-swap validation checks on the user call. +3. The entry point contract calls `swap` on the relevant swap adapter contract, sending the coin to swap to the swap adapter contract. +4. The swap adapter contract swaps the coin sent by the entry point contract to the desired output denom through the relevant swap venue. +5. The swap adapter contract calls `transfer_funds_back` on itself, which transfers the post-swap contract balance back to the entry point contract. +6. The entry point contract performs post-swap validation checks, ensuring the minimum amount out specified in the original call is satisfied. +7. The entry point contract calls `ibc_transfer` on the IBC transfer adapter contract. + - Note: The entry point contract dispatches one of three post swap actions. This simplified example flow is just showing the IBC transfer post swap action. +8. The IBC transfer adapter contract dispatches the IBC transfer. Bon voyage! # Repository Structure diff --git a/assets/skip_swap_flow.png b/assets/skip_swap_flow.png new file mode 100644 index 00000000..a05954a3 Binary files /dev/null and b/assets/skip_swap_flow.png differ diff --git a/skip_swirl.png b/assets/skip_swirl.png similarity index 100% rename from skip_swirl.png rename to assets/skip_swirl.png diff --git a/contracts/entry-point/README.md b/contracts/entry-point/README.md new file mode 100644 index 00000000..915d0e94 --- /dev/null +++ b/contracts/entry-point/README.md @@ -0,0 +1,169 @@ +# Entry Point Contract + +The entry point contract is responsible for providing a standardized interface (w/ safety checks) to interact with Skip Swap across all CosmWasm-enabled chains. The contract: +1. Performs basic validation on the call data +2. If a fee swap is provided, queries the swap adapter contract to determine how much of the coin sent with the contract call is needed to receive the required fee coin(s), and dispatches the swap. +3. Dispatches the user swap provided in the call data to the relevant swap adapter contract. +4. Verifies the amount out received from the swap(s) is greater than the minimum amount required by the caller after all fees have been subtracted (swap, ibc, affiliate) +5. Dispatches one of the following post-swap actions with the received funds from the swap: + - Transfer to an address on the same chain + - IBC transfer to an address on a different chain (which allows for multi-hop IBC transfers or contract calls if the destination chains support it) + - Call a contract on the same chain + +WARNING: Do not send funds directly to the entry point contract without calling one of its functions. Funds sent directly to the contract do not trigger any contract logic that performs validation / safety checks (as the Cosmos SDK handles direct fund transfers in the `Bank` module and not the `Wasm` module). There are no explicit recovery mechanisms for accidentally sent funds. + +## InstantiateMsg + +Instantiates a new entry point contract using the adapter contracts provided in the instantiation message. + +``` json +{ + "swap_venues": [ + { + "name": "neutron-astroport", + "adapter_contract_address": "neutron..." + } + ], + "ibc_transfer_contract_address": "neutron..." +} +``` + +## ExecuteMsg + +### `swap_and_action` + +Swaps the coin sent and performs a post-swap action. + +Optional fields: +- `fee_swap` is used if a fee is required by the IBC transfer. + +Notes: +- Only one coin can be sent to the contract when calling `swap_and_action` otherwise the transaction will fail. +- `timeout_timestamp` is Unix epoch time in nanoseconds. The transaction will fail if the `timeout_timestamp` has passed when the contract is called. +- The `coin_in` field in `user_swap` is optional. If provided, the contract will attempt to swap the provided `coin_in`. If not provided, the contract will attempt to swap the coin that was sent to the contract by the caller minus the amount of that coin used by the `fee_swap`. +- `post_swap_action` can be one of three actions: `bank_send`, `ibc_transfer`, or `contract_call`. + - `bank_send`: Sends the assets received from the `user_swap` to an address on the same chain the swap occured on. + - `ibc_transfer`: ICS-20 transfers the assets received from the swap(s) to an address on a different chain than the swap occured on. The ICS-20 transfer supports including a memo in the outgoing transfer, allowing for multi-hop transfers via Packet Forward Middleware and/or contract calls via IBC-hooks. + - `contract_call`: Calls a contract on the same chain the swap occured, using the assets received from the swap as the contract call's funds. +- `affiliates` is a list of affiliates that will take a fee (in basis points) from the coin received from the `user_swap`. If no affiliates are associated with a call then an empty list is to be provided. + +``` json +{ + "swap_and_action": { + "fee_swap": { + "swap_venue_name": "neutron-astroport", + "coin_out": { + "denom": "untrn", + "amount": "200000" + }, + "operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + } + ] + }, + "user_swap": { + "swap_venue_name": "neutron-astroport", + "coin_in": { + "denom": "uatom", + "amount": "1000000" + }, + "operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + }, + { + "pool": "neutron...", + "denom_in": "untrn", + "denom_out": "uosmo" + } + ] + }, + "min_coin": { + "denom": "uosmo", + "amount": "1000000" + }, + "timeout_timestamp": 1000000000000, + "post_swap_action": { + "bank_send": { + "to_address": "neutron..." + } + }, + "affiliates": [ + { + "basis_points_fee": 10, + "address": "neutron..." + } + ] + } +} +``` + +### `post_swap_action` + +Performs a post swap action. + +Note: Can only be called by the entry point contract itself, any external calls to this function will fail. + +``` json +{ + "post_swap_action": { + "min_coin": { + "denom": "uosmo", + "amount": "1000000" + }, + "timeout_timestamp": 1000000000000, + "post_swap_action": { + "bank_send": { + "to_address": "neutron..." + } + }, + "affiliates": [ + { + "basis_points_fee": 10, + "address": "neutron..." + } + ] + } +} +``` + +## QueryMsg + +### `swap_venue_adapter_contract` + +Returns the swap adapter contract set at instantiation for the given swap venue name provided as an argument. + +Query: +``` json +{ + "swap_venue_adapter_contract": { + "name": "neutron-astroport" + } +} +``` + +Response: +``` json +"neutron..." +``` + +### `ibc_transfer_adapter_contract` + +Returns the IBC transfer adapter contract set at instantiation, requires no arguments. + +Query: +``` json +{ + "ibc_transfer_adapter_contract": {} +} +``` + +Response: +``` json +"neutron..." +``` \ No newline at end of file diff --git a/contracts/networks/neutron/ibc-transfer/README.md b/contracts/networks/neutron/ibc-transfer/README.md new file mode 100644 index 00000000..661cddfb --- /dev/null +++ b/contracts/networks/neutron/ibc-transfer/README.md @@ -0,0 +1,94 @@ +# Neutron IBC Transfer Adapter Contract + +The Neutron IBC Transfer adapter contract is responsible for: +1. Dispatching the IBC transfer with the appropriate IBC fees. +2. Failing the entire transaction if the IBC transfer errors on the swap chain (sending the caller back their original funds). +3. Refunding the caller on the swap chain if the IBC transfer errors or times out once it reaches the destination chain, including refunding unused IBC fees. + +WARNING: Do not send funds directly to the contract without calling one of its functions. Funds sent directly to the contract do not trigger any contract logic that performs validation / safety checks (as the Cosmos SDK handles direct fund transfers in the `Bank` module and not the `Wasm` module). There are no explicit recovery mechanisms for accidentally sent funds. + +## InstantiateMsg + +Instantiates a new Neutron IBC Transfer adapter contract. + +``` json +{} +``` + +## ExecuteMsg + +### `ibc_transfer` + +Dispatches an ICS-20 IBC Transfer given the parameters provided in the contract call. + +``` json +{ + "ibc_transfer": { + "info": { + "source_channel": "channel-1", + "receiver": "cosmos...", + "fee": { + "recv_fee": [], + "ack_fee": [ + { + "denom": "untrn", + "amount": "100000" + } + ], + "timeout_fee": [ + { + "denom": "untrn", + "amount": "100000" + } + ] + }, + "memo": "", + "recover_address": "neutron..." + }, + "coin": { + "denom": "uatom", + "amount": "1000000" + }, + "timeout_timestamp": 1000000000000 + } +} +``` + +## QueryMsg + +### `in_progress_ibc_transfer` + +Returns the in progress ibc transfer info associated with the given `channel_id` and `sequence_id` (which make up a unique identifier mapped to in progress ibc transfers in the sub msg reply handler). + +Query: +``` json +{ + "in_progress_ibc_transfer": { + "channel_id": "channel-1", + "sequence_id": 420 + } +} +``` + +Response: +``` json +{ + "recover_address": "neutron...", + "coin": { + "denom": "uatom", + "amount": "1000000" + }, + "ack_fee": [ + { + "denom": "untrn", + "amount": "100000" + } + ], + "timeout_fee": [ + { + "denom": "untrn", + "amount": "100000" + } + ] +} +``` \ No newline at end of file diff --git a/contracts/networks/neutron/swap/README.md b/contracts/networks/neutron/swap/README.md new file mode 100644 index 00000000..a51ff5c9 --- /dev/null +++ b/contracts/networks/neutron/swap/README.md @@ -0,0 +1,139 @@ +# Neutron Astroport Swap Adapter Contract + +The Neutron Astroport swap adapter contract is responsible for: +1. Taking the standardized entry point swap operations message format and converting it to the Astroport Router `SwapOperation` format. +2. Swapping by calling the Astroport router. +3. Providing query methods that can be called by the entry point contract (generally, to any external actor) to simulate multi-hop swaps that either specify an exact amount in (estimating how much would be received from the swap) or an exact amount out (estimating how much is required to get the specified amount out). + +Note: Swap adapter contracts expect to be called by an entry point contract that provides basic validation and minimum amount out safety guarantees for the caller. There are no slippage guarantees provided by swap adapter contracts. + +WARNING: Do not send funds directly to the contract without calling one of its functions. Funds sent directly to the contract do not trigger any contract logic that performs validation / safety checks (as the Cosmos SDK handles direct fund transfers in the `Bank` module and not the `Wasm` module). There are no explicit recovery mechanisms for accidentally sent funds. + +## InstantiateMsg + +Instantiates a new Neutron Astroport swap adapter contract using the Astroport router provided in the instantiation message. + +``` json +{ + "router_contract_address": "neutron..." +} +``` + +## ExecuteMsg + +### `swap` + +Swaps the coin sent using the operations provided. + +``` json +{ + "swap": { + "operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + }, + { + "pool": "neutron...", + "denom_in": "untrn", + "denom_out": "uosmo" + } + ] + } +} +``` + +### `transfer_funds_back` + +Transfers all contract funds to the address provided, called by the swap adapter contract to send back the entry point contract the assets received from swapping. + +Note: This function can be called by anyone as the contract is assumed to have no balance before/after it's called by the entry point contract. Do not send funds directly to this contract without calling a function. + +``` json +{ + "transfer_funds_back": { + "caller": "neutron..." + } +} +``` + +## QueryMsg + +### `router_contract_address` + +Returns the Astroport router contract address set at instantiation. + +Query: +``` json +{ + "router_contract_address": {} +} +``` + +Response: +``` json +"neutron..." +``` + +### `simulate_swap_exact_coin_out` + +Returns the coin in required to receive the `coin_out` specified in the call (swapped through the `swap_operatons` provided) + +Query: +``` json +{ + "simulate_swap_exact_coin_out": { + "coin_out": { + "denom": "untrn", + "amount": "200000" + }, + "swap_operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + } + ] + } +} +``` + +Response: +``` json +{ + "denom": "uatom", + "amount": "100" +} +``` + +### `simulate_swap_exact_coin_in` + +Returns the coin out that would be received from swapping the `coin_in` specified in the call (swapped through the `swap_operatons` provided) + +Query: +``` json +{ + "simulate_swap_exact_coin_in": { + "coin_in": { + "denom": "uatom", + "amount": "100" + }, + "swap_operations": [ + { + "pool": "neutron...", + "denom_in": "uatom", + "denom_out": "untrn" + } + ] + } +} +``` + +Response: +``` json +{ + "denom": "untrn", + "amount": "100000" +} +``` \ No newline at end of file diff --git a/contracts/networks/osmosis/ibc-transfer/README.md b/contracts/networks/osmosis/ibc-transfer/README.md new file mode 100644 index 00000000..a70667cd --- /dev/null +++ b/contracts/networks/osmosis/ibc-transfer/README.md @@ -0,0 +1,75 @@ +# Osmosis IBC Transfer Adapter Contract + +The Osmosis IBC Transfer adapter contract is responsible for: +1. Dispatching the IBC transfer. +2. Failing the entire transaction if the IBC transfer errors on the swap chain (sending the caller back their original funds). +3. Refunding the caller on the swap chain if the IBC transfer errors or times out once it reaches the destination chain. + +WARNING: Do not send funds directly to the contract without calling one of its functions. Funds sent directly to the contract do not trigger any contract logic that performs validation / safety checks (as the Cosmos SDK handles direct fund transfers in the `Bank` module and not the `Wasm` module). There are no explicit recovery mechanisms for accidentally sent funds. + +## InstantiateMsg + +Instantiates a new Osmosis IBC Transfer adapter contract. + +``` json +{} +``` + +## ExecuteMsg + +### `ibc_transfer` + +Dispatches an ICS-20 IBC Transfer given the parameters provided in the contract call. + +Note: Fees sent as parameters with the contract call are unused by the contract since Osmosis currently does not require ICS-29 fees for outgoing ibc transfers. The fee field is still included in the call data to keep the interface the same across all IBC transfer adapter contracts. + +``` json +{ + "ibc_transfer": { + "info": { + "source_channel": "channel-1", + "receiver": "cosmos...", + "fee": { + "recv_fee": [], + "ack_fee": [], + "timeout_fee": [] + }, + "memo": "", + "recover_address": "osmo..." + }, + "coin": { + "denom": "uosmo", + "amount": "1000000" + }, + "timeout_timestamp": 1000000000000 + } +} +``` + +## QueryMsg + +### `in_progress_ibc_transfer` + +Returns the in progress ibc transfer info associated with the given `channel_id` and `sequence_id` (which make up a unique identifier mapped to in progress ibc transfers in the sub msg reply handler). + +Query: +``` json +{ + "in_progress_ibc_transfer": { + "channel_id": "channel-1", + "sequence_id": 420 + } +} +``` + +Response: +``` json +{ + "recover_address": "osmo...", + "coin": { + "denom": "uosmo", + "amount": "1000000" + }, + "channel_id": "channel-1" +} +``` \ No newline at end of file diff --git a/contracts/networks/osmosis/swap/README.md b/contracts/networks/osmosis/swap/README.md new file mode 100644 index 00000000..fd2d0b5c --- /dev/null +++ b/contracts/networks/osmosis/swap/README.md @@ -0,0 +1,123 @@ +# Osmosis Poolmanager Swap Adapter Contract + +The Osmosis Poolmanager swap adapter contract is responsible for: +1. Taking the standardized entry point swap operations message format and converting it to the Osmosis Poolmanager `SwapAmountInRoute` format. +2. Swapping by calling the Osmosis Poolmanager module. +3. Providing query methods that can be called by the entry point contract (generally, to any external actor) to simulate multi-hop swaps that either specify an exact amount in (estimating how much would be received from the swap) or an exact amount out (estimating how much is required to get the specified amount out). + +Note: Swap adapter contracts expect to be called by an entry point contract that provides basic validation and minimum amount out safety guarantees for the caller. There are no slippage guarantees provided by swap adapter contracts. + +WARNING: Do not send funds directly to the contract without calling one of its functions. Funds sent directly to the contract do not trigger any contract logic that performs validation / safety checks (as the Cosmos SDK handles direct fund transfers in the `Bank` module and not the `Wasm` module). There are no explicit recovery mechanisms for accidentally sent funds. + +## InstantiateMsg + +Instantiates a new Osmosis Poolmanager swap adapter contract. + +``` json +{} +``` + +## ExecuteMsg + +### `swap` + +Swaps the coin sent using the operations provided. + +Note: The `pool` string field provided in the operations must be able to be converted into a `u64` (the format used by Osmosis for pool IDs) + +``` json +{ + "swap": { + "operations": [ + { + "pool": "1", + "denom_in": "uosmo", + "denom_out": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" + }, + { + "pool": "2", + "denom_in": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "denom_out": "ibc/987C17B11ABC2B20019178ACE62929FE9840202CE79498E29FE8E5CB02B7C0A4" + } + ] + } +} +``` + +### `transfer_funds_back` + +Transfers all contract funds to the address provided, called by the swap adapter contract to send back the entry point contract the assets received from swapping. + +Note: This function can be called by anyone as the contract is assumed to have no balance before/after it's called by the entry point contract. Do not send funds directly to this contract without calling a function. + +``` json +{ + "transfer_funds_back": { + "caller": "osmo..." + } +} +``` + +## QueryMsg + +### `simulate_swap_exact_coin_out` + +Returns the coin in required to receive the `coin_out` specified in the call (swapped through the `swap_operatons` provided) + +Query: +``` json +{ + "simulate_swap_exact_coin_out": { + "coin_out": { + "denom": "uosmo", + "amount": "200000" + }, + "swap_operations": [ + { + "pool": "1", + "denom_in": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "denom_out": "uosmo" + } + ] + } +} +``` + +Response: +``` json +{ + "denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "amount": "100" +} +``` + +### `simulate_swap_exact_coin_in` + +Returns the coin out that would be received from swapping the `coin_in` specified in the call (swapped through the `swap_operatons` provided) + +Query: +``` json +{ + "simulate_swap_exact_coin_in": { + "coin_in": { + "denom": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "amount": "100" + }, + "swap_operations": [ + { + "pool": "1", + "denom_in": "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + "denom_out": "uosmo" + } + ] + } +} +``` + +Response: +``` json +{ + "denom": "uosmo", + "amount": "100000" +} +``` \ No newline at end of file