From ffc54df6eb94ee750ddd7fee36fdef0122895d1d Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Wed, 19 Oct 2022 17:53:22 +0800 Subject: [PATCH 01/84] create a clean copy to for PR --- spec/app/ics-100-atomic-swap/README.md | 443 +++++++++++++++++++++++ spec/app/ics-100-atomic-swap/ibcswap.png | 3 + 2 files changed, 446 insertions(+) create mode 100644 spec/app/ics-100-atomic-swap/README.md create mode 100644 spec/app/ics-100-atomic-swap/ibcswap.png diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md new file mode 100644 index 000000000..a1250984f --- /dev/null +++ b/spec/app/ics-100-atomic-swap/README.md @@ -0,0 +1,443 @@ +--- +ics: 100 +title: Atomic Swap +stage: draft +category: IBC/APP +requires: 25, 26 +kind: instantiation +author: Ping Liang , Edward Gunawan +created: 2022-07-27 +modified: 2022-10-07 +--- + +## Synopsis + +This standard document specifies packet data structure, state machine handling logic, and encoding details for the atomic swap of fungible tokens over an IBC channel between two modules on separate chains. + +### Motivation + +Users may wish to exchange tokens without transfering tokens away from its native chain. ICS-100 enabled chains can facilitate atomic swaps between users and their tokens located on the different chains. This is useful for exchanges between specific users at specific prices, and opens opportunities for new application designs. + +### Definitions + +`Atomic Swap`: An exchange of tokens from separate chains without transfering tokens away from its source chain. + +`Order`: an offer to exchange quantity X of token A for quantity Y of token B. Tokens offered are sent to an escrow account (owned by the module) + +`Maker`: A user that makes or initiates an order. + +`Taker`: Is the counterparty who takes or responds to an order. + +### Desired Properties + +- `Permissionless`: no need to whitelist connections, modules, or denominations. +- `Gaurantee of exchange`: no occurence of a user receiving tokens without the equivalent promised exchange. +- `Escrow enabled`: an account owned by the module will hold tokens and facilitate exchange. +- `Refundable`: tokens are refunded by escrow when an order is cancelled +- `Basic orderbook`: a store of orders functioning as an orderbook system + +## Technical Specification + +### General Design + +A user offers tokens for exchange by making an order. The order specifies the quantity and price of exchange, and sends the offered tokens to the chain's escrow account. + +Any user on a different chain with the correct token denomination can accept the offer by taking the order. The taker sends the desired amount of tokens to the chain's escrow account. + +The escrow account on each respective chain transfers the corresponding token amounts to each user's receiving address, without requiring the usual ibc transfer. + +### Data Structures + +Only one packet data type is required: `AtomicSwapPacketData`, which specifies the swap message type, data(protobuf marshalled) and a memo field. + +```typescript +enum SwapMessageType { + // Default zero value enumeration + TYPE_UNSPECIFIED = 0, + TYPE_MSG_MAKE_SWAP = 1, + TYPE_MSG_TAKE_SWAP = 2, + TYPE_MSG_CANCEL_SWAP = 3, +} + +// AtomicSwapPacketData is comprised of a swap message type, raw transaction and optional memo field. +interface AtomicSwapPacketData { + type: SwapMessageType; + data: types[]; + memo: string; +} +``` + +All `AtomicSwapPacketData` will be forwarded to the corresponding message handler to execute according to its type. There are 3 types: + +```typescript +interface MakeSwap { + // the port on which the packet will be sent + source_port string + // the channel by which the packet will be sent + source_channel: string; + // the tokens to be exchanged + sell_token : Coin + buy_token: Coin; + // the sender address + maker_address: string; + // the sender's address on the destination chain + maker_receiving_address string; + // if desired_taker is specified, + // only the desired_taker is allowed to take this order + // this is address on destination chain + desired_taker: string; + create_timestamp: int64; +} +``` + +```typescript +interface TakeSwap { + order_id: string; + // the tokens to be sell + sell_token: Coin; + // the sender address + taker_address: string; + // the sender's address on the destination chain + taker_receiving_address: string; + create_timestamp: int64; +} +``` + +```typescript +interface CancelSwap { + order_id: string; + maker_address: string; +} +``` + +Both the source chain and destination chain maintain separate orderbooks. Orders are saved in both source chain and destination chain. + +```typescript +enum Status { + INITIAL = 0, + SYNC = 1, + CANCEL = 2, + COMPLETE = 3, +} + +interface OrderBook { + id: string; + maker: MakeSwap; + status: Status; + channel_id: string; + takers: TakeSwap[]; + cancel_timestamp: int64; + complete_timestamp: int64; +} +``` + +### Life scope and control flow + +#### Making a swap + +1. User creates an order on the source chain with specified parameters (see type `MakeSwap`). Tokens are sent to the escrow address owned by the module. The order is saved on the source chain +2. An `AtomicSwapPacketData` is relayed to the destination chain where `onRecvPacket` the order is also saved on the destination chain. +3. A packet is subsequently relayed back for acknowledgement. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. + +#### Taking a swap + +1. A user takes an order on the destination chain by triggering `TakeSwap`. Tokens are sent to the escrow address owned by the module. +2. An `AtomicSwapPacketData` is relayed to the source chain where `onRecvPacket` the escrowed tokens are sent to the destination address. +3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the destination chain is sent to the related destination address. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. + +#### Cancelling a swap + +1. The taker cancels a previously created order. +2. An `AtomicSwapPacketData` is relayed to the source chain where `onRecvPacket` the order is cancelled on the destination chain. +3. A packet is relayed back where upon acknowledgement the order on the source chain is also cancelled. + +### Sub-protocols + +The sub-protocols described herein should be implemented in a "Fungible Token Swap" module with access to a bank module and to the IBC routing module. + +```ts +function createSwap(request MakeSwap) { + +} +``` + +```ts +function fillSwap(request TakeSwap) { + +} +``` + +```ts +function cancelSwap(request CancelSwap) { + +} +``` + +#### Port & channel setup + +The fungible token swap module on a chain must always bind to a port with the id `atomicswap` + +The `setup` function must be called exactly once when the module is created (perhaps when the blockchain itself is initialised) to bind to the appropriate port and create an escrow address (owned by the module). + +```typescript +function setup() { + capability = routingModule.bindPort("atomicswap", ModuleCallbacks{ + onChanOpenInit, + onChanOpenTry, + onChanOpenAck, + onChanOpenConfirm, + onChanCloseInit, + onChanCloseConfirm, + onRecvPacket, + onTimeoutPacket, + onAcknowledgePacket, + onTimeoutPacketClose + }) + claimCapability("port", capability) +} +``` + +Once the setup function has been called, channels can be created via the IBC routing module. + +#### Channel lifecycle management + +An fungible token swap module will accept new channels from any module on another machine, if and only if: + +- The channel being created is unordered. +- The version string is `ics100-1`. + +```typescript +function onChanOpenInit( + order: ChannelOrder, + connectionHops: [Identifier], + portIdentifier: Identifier, + channelIdentifier: Identifier, + counterpartyPortIdentifier: Identifier, + counterpartyChannelIdentifier: Identifier, + version: string) => (version: string, err: Error) { + // only unordered channels allowed + abortTransactionUnless(order === UNORDERED) + // assert that version is "ics100-1" or empty + // if empty, we return the default transfer version to core IBC + // as the version for this channel + abortTransactionUnless(version === "ics100-1" || version === "") + + return "ics100-1", nil +} +``` + +```typescript +function onChanOpenTry( + order: ChannelOrder, + connectionHops: [Identifier], + portIdentifier: Identifier, + channelIdentifier: Identifier, + counterpartyPortIdentifier: Identifier, + counterpartyChannelIdentifier: Identifier, + counterpartyVersion: string) => (version: string, err: Error) { + // only unordered channels allowed + abortTransactionUnless(order === UNORDERED) + // assert that version is "ics100-1" + abortTransactionUnless(counterpartyVersion === "ics100-1") + + return "ics100-1", nil +} +``` + +```typescript +function onChanOpenAck( + portIdentifier: Identifier, + channelIdentifier: Identifier, + counterpartyChannelIdentifier: Identifier, + counterpartyVersion: string +) { + // port has already been validated + // assert that counterparty selected version is "ics31-1" + abortTransactionUnless(counterpartyVersion === "ics100-1"); +} +``` + +```typescript +function onChanOpenConfirm(portIdentifier: Identifier, channelIdentifier: Identifier) { + // accept channel confirmations, port has already been validated, version has already been validated +} +``` + +```typescript +function onChanCloseInit(portIdentifier: Identifier, channelIdentifier: Identifier) { + // always abort transaction + abortTransactionUnless(FALSE); +} +``` + +```typescript +function onChanCloseConfirm(portIdentifier: Identifier, channelIdentifier: Identifier) { + // no action necessary +} +``` + +#### Packet relay + +`sendAtomicSwapPacket` must be called by a transaction handler in the module which performs appropriate signature checks, specific to the account owner on the host state machine. + +```typescript +function sendAtomicSwapPacket(swapPacket AtomicSwapPacketData) { + + // send packet using the interface defined in ICS4 + handler.sendPacket( + getCapability("port"), + sourcePort, + sourceChannel, + timeoutHeight, + timeoutTimestamp, + swapPacket.getBytes(), // Should be proto marshalled bytes. + ) +} +``` + +`onRecvPacket` is called by the routing module when a packet addressed to this module has been received. + +```typescript +function onRecvPacket(data: AtomicSwapPacketData) { + switch data.Type { + case TYPE_MSG_MAKE_SWAP: + var msg types.MsgMakeSwapRequest + + if err := types.ModuleCdc.Unmarshal(data.Data, &msg); err != nil { + return err + } + if err := k.executeMakeSwap(ctx, packet, &msg); err != nil { + return err + } + + return nil + + case TYPE_MSG_TAKE_SWAP: + var msg types.MsgTakeSwapRequest + + if err := types.ModuleCdc.Unmarshal(data.Data, &msg); err != nil { + return err + } + if err2 := k.executeTakeSwap(ctx, packet, &msg); err2 != nil { + return err2 + } else { + return nil + } + + case TYPE_MSG_CANCEL_SWAP: + var msg types.MsgCancelSwapRequest + + if err := types.ModuleCdc.Unmarshal(data.Data, &msg); err != nil { + return err + } + if err2 := k.executeCancelSwap(ctx, packet, &msg); err2 != nil { + return err2 + } else { + return nil + } + + default: + return types.ErrUnknownDataPacket + } + + ctx.EventManager().EmitTypedEvents(&data) + + return nil +} +``` + +`onAcknowledgePacket` is called by the routing module when a packet sent by this module has been acknowledged. + +```typescript +function onAcknowledgePacket( + packet: AtomicSwapPacketData, + acknowledgement: bytes) { +switch ack.Response.(type) { + case *channeltypes.Acknowledgement_Error: + return k.refundPacketToken(ctx, packet, data) + default: + switch data.Type { + case TYPE_MSG_TAKE_SWAP: + var msg types.MsgTakeSwapRequest + + if err := types.ModuleCdc.Unmarshal(data.Data, &msg); err != nil { + return err + } + // check order status + if order, ok := k.GetLimitOrder(ctx, msg.OrderId); ok { + k.executeTakeSwap(ctx, order, &msg, StepAcknowledgement) + } else { + return types.ErrOrderDoesNotExists + } + break + + case TYPE_MSG_CANCEL_SWAP: + var msg types.MsgCancelSwapRequest + + if err := types.ModuleCdc.Unmarshal(data.Data, &msg); err != nil { + return err + } + if err2 := k.executeCancel(ctx, &msg, StepAcknowledgement); err2 != nil { + return err2 + } else { + return nil + } + break + } + } + return nil +} +``` + +`onTimeoutPacket` is called by the routing module when a packet sent by this module has timed-out (such that the tokens will be refunded). + +```typescript +function onTimeoutPacket(packet: AtomicSwapPacketData) { + // the packet timed-out, so refund the tokens + refundTokens(packet); +} +``` + +`refundTokens` is called by both `onAcknowledgePacket` on failure, and `onTimeoutPacket`, to refund escrowed tokens to the original owner. + +```typescript +function refundTokens(packet: AtomicSwapPacketData) { + FungibleTokenPacketData data = packet.data + //send tokens from module to message sender +} +``` + +```typescript +function onTimeoutPacketClose(packet: AtomicSwapPacketData) { + // can't happen, only unordered channels allowed +} +``` + +## Backwards Compatibility + +Not applicable. + +## Forwards Compatibility + +This initial standard uses version "ics100-1" in the channel handshake. + +A future version of this standard could use a different version in the channel handshake, +and safely alter the packet data format & packet handler semantics. + +## Example Implementation + +https://github.com/ibcswap/ibcswap + +## Other Implementations + +Coming soon. + +## History + +Aug 15, 2022 - Draft written + +Oct 6, 2022 - Draft revised + +## Copyright + +All content herein is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). diff --git a/spec/app/ics-100-atomic-swap/ibcswap.png b/spec/app/ics-100-atomic-swap/ibcswap.png new file mode 100644 index 000000000..51aa1cc53 --- /dev/null +++ b/spec/app/ics-100-atomic-swap/ibcswap.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7fbd697de572f600be7942adb553f897e3e2b92aa0066ccf23b5d331e17f9b2c +size 60791 From 90a8ef26ddbe165511870e3efb852d7be38d1e1f Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Thu, 3 Nov 2022 07:43:12 +0800 Subject: [PATCH 02/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Marius Poke --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index a1250984f..a9afbd237 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -44,7 +44,7 @@ A user offers tokens for exchange by making an order. The order specifies the qu Any user on a different chain with the correct token denomination can accept the offer by taking the order. The taker sends the desired amount of tokens to the chain's escrow account. -The escrow account on each respective chain transfers the corresponding token amounts to each user's receiving address, without requiring the usual ibc transfer. +The escrow account on each respective chain transfers the corresponding token amounts to each user's local receiving address, without requiring the usual ibc transfer. ### Data Structures From a0b8455c756990aa94937a8dd8a6156a70495f2e Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Thu, 3 Nov 2022 07:44:27 +0800 Subject: [PATCH 03/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Marius Poke --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index a9afbd237..0e342cf27 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -392,7 +392,7 @@ switch ack.Response.(type) { `onTimeoutPacket` is called by the routing module when a packet sent by this module has timed-out (such that the tokens will be refunded). ```typescript -function onTimeoutPacket(packet: AtomicSwapPacketData) { +function onTimeoutPacket(packet: Packet) { // the packet timed-out, so refund the tokens refundTokens(packet); } From f35e89ca9256e4e472294d8b15632d5f50769c4e Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Thu, 3 Nov 2022 07:46:04 +0800 Subject: [PATCH 04/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Marius Poke --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 0e342cf27..9caa43b50 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -143,7 +143,7 @@ interface OrderBook { 1. A user takes an order on the destination chain by triggering `TakeSwap`. Tokens are sent to the escrow address owned by the module. 2. An `AtomicSwapPacketData` is relayed to the source chain where `onRecvPacket` the escrowed tokens are sent to the destination address. -3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the destination chain is sent to the related destination address. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. +3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the destination chain are sent to the related destination address. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Cancelling a swap From 806a3e66f471d6cf7d81581562246e3e7f9199a2 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Thu, 3 Nov 2022 07:46:53 +0800 Subject: [PATCH 05/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Marius Poke --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 9caa43b50..18a1edfff 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -252,7 +252,7 @@ function onChanOpenAck( counterpartyVersion: string ) { // port has already been validated - // assert that counterparty selected version is "ics31-1" + // assert that counterparty selected version is "ics100-1" abortTransactionUnless(counterpartyVersion === "ics100-1"); } ``` From a3981918cf64a413fc371304b1e36a6a4a82dd38 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Thu, 3 Nov 2022 07:59:28 +0800 Subject: [PATCH 06/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Marius Poke --- spec/app/ics-100-atomic-swap/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 18a1edfff..16d7a47cc 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -401,8 +401,8 @@ function onTimeoutPacket(packet: Packet) { `refundTokens` is called by both `onAcknowledgePacket` on failure, and `onTimeoutPacket`, to refund escrowed tokens to the original owner. ```typescript -function refundTokens(packet: AtomicSwapPacketData) { - FungibleTokenPacketData data = packet.data +function refundTokens(packet: Packet) { + AtomicSwapPacketData data = packet.data //send tokens from module to message sender } ``` From a990bc8c6fbbb6df999c552c084d409a728889e3 Mon Sep 17 00:00:00 2001 From: EddyG Date: Sat, 5 Nov 2022 10:57:25 +0700 Subject: [PATCH 07/84] incorporate feedbacks --- spec/app/ics-100-atomic-swap/README.md | 44 +++++++++++++++----------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 16d7a47cc..78b9c0161 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -20,7 +20,7 @@ Users may wish to exchange tokens without transfering tokens away from its nativ ### Definitions -`Atomic Swap`: An exchange of tokens from separate chains without transfering tokens away from its source chain. +`Atomic Swap`: An exchange of tokens from separate chains without transfering tokens from one blockchain to another. `Order`: an offer to exchange quantity X of token A for quantity Y of token B. Tokens offered are sent to an escrow account (owned by the module) @@ -28,6 +28,10 @@ Users may wish to exchange tokens without transfering tokens away from its nativ `Taker`: Is the counterparty who takes or responds to an order. +`Maker Chain`: The blockchain where a maker makes or initiaties an order. + +`Taker Chain`: The blockchain where a taker takes or responds to an order. + ### Desired Properties - `Permissionless`: no need to whitelist connections, modules, or denominations. @@ -40,11 +44,13 @@ Users may wish to exchange tokens without transfering tokens away from its nativ ### General Design -A user offers tokens for exchange by making an order. The order specifies the quantity and price of exchange, and sends the offered tokens to the chain's escrow account. + + +A maker offers token A in exchange for token B by making an order. The order specifies the quantity and price of exchange, and sends the offered token A to the maker chain's escrow account. -Any user on a different chain with the correct token denomination can accept the offer by taking the order. The taker sends the desired amount of tokens to the chain's escrow account. +Any taker on a different chain with token B can accept the offer by taking the order. The taker sends the desired amount of token B to the taker chain's escrow account. -The escrow account on each respective chain transfers the corresponding token amounts to each user's local receiving address, without requiring the usual ibc transfer. +The escrow account on each respective chain transfers the corresponding token amounts to each user's receiving address, without requiring the usual ibc transfer. ### Data Structures @@ -80,11 +86,11 @@ interface MakeSwap { buy_token: Coin; // the sender address maker_address: string; - // the sender's address on the destination chain + // the sender's address on the taker chain maker_receiving_address string; // if desired_taker is specified, // only the desired_taker is allowed to take this order - // this is address on destination chain + // this is address on the taker chain desired_taker: string; create_timestamp: int64; } @@ -97,7 +103,7 @@ interface TakeSwap { sell_token: Coin; // the sender address taker_address: string; - // the sender's address on the destination chain + // the sender's address on the taker chain taker_receiving_address: string; create_timestamp: int64; } @@ -110,7 +116,7 @@ interface CancelSwap { } ``` -Both the source chain and destination chain maintain separate orderbooks. Orders are saved in both source chain and destination chain. +Both the maker chain and taker chain maintain separate orderbooks. Orders are saved in both maker chain and taker chain. ```typescript enum Status { @@ -135,21 +141,21 @@ interface OrderBook { #### Making a swap -1. User creates an order on the source chain with specified parameters (see type `MakeSwap`). Tokens are sent to the escrow address owned by the module. The order is saved on the source chain -2. An `AtomicSwapPacketData` is relayed to the destination chain where `onRecvPacket` the order is also saved on the destination chain. +1. User creates an order on the maker chain with specified parameters (see type `MakeSwap`). Tokens are sent to the escrow address owned by the module. The order is saved on the maker chain +2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is also saved on the taker chain. 3. A packet is subsequently relayed back for acknowledgement. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Taking a swap -1. A user takes an order on the destination chain by triggering `TakeSwap`. Tokens are sent to the escrow address owned by the module. -2. An `AtomicSwapPacketData` is relayed to the source chain where `onRecvPacket` the escrowed tokens are sent to the destination address. -3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the destination chain are sent to the related destination address. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. +1. A user takes an order on the taker chain by triggering `TakeSwap`. Tokens are sent to the escrow address owned by the module. +2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the destination address. +3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain is sent to the related destination address. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Cancelling a swap 1. The taker cancels a previously created order. -2. An `AtomicSwapPacketData` is relayed to the source chain where `onRecvPacket` the order is cancelled on the destination chain. -3. A packet is relayed back where upon acknowledgement the order on the source chain is also cancelled. +2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the order is cancelled on the taker chain. +3. A packet is relayed back where upon acknowledgement the order on the maker chain is also cancelled. ### Sub-protocols @@ -252,7 +258,7 @@ function onChanOpenAck( counterpartyVersion: string ) { // port has already been validated - // assert that counterparty selected version is "ics100-1" + // assert that counterparty selected version is "ics31-1" abortTransactionUnless(counterpartyVersion === "ics100-1"); } ``` @@ -392,7 +398,7 @@ switch ack.Response.(type) { `onTimeoutPacket` is called by the routing module when a packet sent by this module has timed-out (such that the tokens will be refunded). ```typescript -function onTimeoutPacket(packet: Packet) { +function onTimeoutPacket(packet: AtomicSwapPacketData) { // the packet timed-out, so refund the tokens refundTokens(packet); } @@ -401,8 +407,8 @@ function onTimeoutPacket(packet: Packet) { `refundTokens` is called by both `onAcknowledgePacket` on failure, and `onTimeoutPacket`, to refund escrowed tokens to the original owner. ```typescript -function refundTokens(packet: Packet) { - AtomicSwapPacketData data = packet.data +function refundTokens(packet: AtomicSwapPacketData) { + FungibleTokenPacketData data = packet.data //send tokens from module to message sender } ``` From 38f3115eff3f569ebf5c3f80ee4c9eedd60054db Mon Sep 17 00:00:00 2001 From: EddyG Date: Sat, 5 Nov 2022 11:02:32 +0700 Subject: [PATCH 08/84] incorporate feedbacks --- spec/app/ics-100-atomic-swap/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 78b9c0161..1cdaaef13 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -258,7 +258,7 @@ function onChanOpenAck( counterpartyVersion: string ) { // port has already been validated - // assert that counterparty selected version is "ics31-1" + // assert that counterparty selected version is "ics100-1" abortTransactionUnless(counterpartyVersion === "ics100-1"); } ``` @@ -398,7 +398,7 @@ switch ack.Response.(type) { `onTimeoutPacket` is called by the routing module when a packet sent by this module has timed-out (such that the tokens will be refunded). ```typescript -function onTimeoutPacket(packet: AtomicSwapPacketData) { +function onTimeoutPacket(packet: Packet) { // the packet timed-out, so refund the tokens refundTokens(packet); } @@ -407,8 +407,8 @@ function onTimeoutPacket(packet: AtomicSwapPacketData) { `refundTokens` is called by both `onAcknowledgePacket` on failure, and `onTimeoutPacket`, to refund escrowed tokens to the original owner. ```typescript -function refundTokens(packet: AtomicSwapPacketData) { - FungibleTokenPacketData data = packet.data +function refundTokens(packet: Packet) { + AtomicSwapPacketData data = packet.data //send tokens from module to message sender } ``` From c434e8895aca11a822146289c6033d6b0db6bb0c Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Mon, 7 Nov 2022 14:01:01 +0800 Subject: [PATCH 09/84] remove cancel functions --- spec/app/ics-100-atomic-swap/README.md | 28 +++++--------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 1cdaaef13..169060e2e 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -62,7 +62,6 @@ enum SwapMessageType { TYPE_UNSPECIFIED = 0, TYPE_MSG_MAKE_SWAP = 1, TYPE_MSG_TAKE_SWAP = 2, - TYPE_MSG_CANCEL_SWAP = 3, } // AtomicSwapPacketData is comprised of a swap message type, raw transaction and optional memo field. @@ -73,7 +72,7 @@ interface AtomicSwapPacketData { } ``` -All `AtomicSwapPacketData` will be forwarded to the corresponding message handler to execute according to its type. There are 3 types: +All `AtomicSwapPacketData` will be forwarded to the corresponding message handler to execute according to its type. There are 2 types: ```typescript interface MakeSwap { @@ -93,6 +92,7 @@ interface MakeSwap { // this is address on the taker chain desired_taker: string; create_timestamp: int64; + expired_timestamp: int64; } ``` @@ -109,13 +109,6 @@ interface TakeSwap { } ``` -```typescript -interface CancelSwap { - order_id: string; - maker_address: string; -} -``` - Both the maker chain and taker chain maintain separate orderbooks. Orders are saved in both maker chain and taker chain. ```typescript @@ -144,6 +137,7 @@ interface OrderBook { 1. User creates an order on the maker chain with specified parameters (see type `MakeSwap`). Tokens are sent to the escrow address owned by the module. The order is saved on the maker chain 2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is also saved on the taker chain. 3. A packet is subsequently relayed back for acknowledgement. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. +4. An order should not be taken once the current time is later than expired timestamp, and all expired orders should refund its escrow tokens at `block begin` function(todo: figout the name). #### Taking a swap @@ -151,30 +145,18 @@ interface OrderBook { 2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the destination address. 3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain is sent to the related destination address. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. -#### Cancelling a swap - -1. The taker cancels a previously created order. -2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the order is cancelled on the taker chain. -3. A packet is relayed back where upon acknowledgement the order on the maker chain is also cancelled. - ### Sub-protocols The sub-protocols described herein should be implemented in a "Fungible Token Swap" module with access to a bank module and to the IBC routing module. ```ts -function createSwap(request MakeSwap) { - -} -``` - -```ts -function fillSwap(request TakeSwap) { +function makeSwap(request MakeSwap) { } ``` ```ts -function cancelSwap(request CancelSwap) { +function takeSwap(request TakeSwap) { } ``` From 7d2ed9cd1e51142fa83c8838f19688ebfe8d2b48 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Tue, 8 Nov 2022 12:52:30 +0800 Subject: [PATCH 10/84] re-organize the flow and code --- spec/app/ics-100-atomic-swap/README.md | 223 +++++++++++++++---------- 1 file changed, 136 insertions(+), 87 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 169060e2e..d1bb3f6f2 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -75,7 +75,7 @@ interface AtomicSwapPacketData { All `AtomicSwapPacketData` will be forwarded to the corresponding message handler to execute according to its type. There are 2 types: ```typescript -interface MakeSwap { +interface MakeSwapMsg { // the port on which the packet will be sent source_port string // the channel by which the packet will be sent @@ -97,7 +97,7 @@ interface MakeSwap { ``` ```typescript -interface TakeSwap { +interface TakeSwapMsg { order_id: string; // the tokens to be sell sell_token: Coin; @@ -121,13 +121,29 @@ enum Status { interface OrderBook { id: string; - maker: MakeSwap; + maker: MakeSwapMsg; status: Status; channel_id: string; - takers: TakeSwap[]; + taker: TakeSwap; cancel_timestamp: int64; complete_timestamp: int64; + + createOrder(msg: MakeSwapMsg) OrderBook { + return { + id : generateOrderId(msg) + status: Status.INITIAL + maker: msg, + } + } } + +// Order id is a global unique string +function generateOrderId(msg MakeSwapMsg) { + cosnt bytes = protobuf.encode(msg) + return sha265(bytes) +} + + ``` ### Life scope and control flow @@ -137,7 +153,7 @@ interface OrderBook { 1. User creates an order on the maker chain with specified parameters (see type `MakeSwap`). Tokens are sent to the escrow address owned by the module. The order is saved on the maker chain 2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is also saved on the taker chain. 3. A packet is subsequently relayed back for acknowledgement. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. -4. An order should not be taken once the current time is later than expired timestamp, and all expired orders should refund its escrow tokens at `block begin` function(todo: figout the name). +4. An order should not be taken once the current time is later than expired timestamp, and all expired orders should refund its escrow tokens at `BeginBlock` function. #### Taking a swap @@ -150,14 +166,53 @@ interface OrderBook { The sub-protocols described herein should be implemented in a "Fungible Token Swap" module with access to a bank module and to the IBC routing module. ```ts -function makeSwap(request MakeSwap) { - +function makeSwap(request MakeSwapMsg) { + const balance = bank_keeper.getBalances(request.make_address) + abortTransactionUnless(balance.amount > request.sell_token.Amount) + // found the escrow address by source port and source channel + const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) + // lock the sell_token to escrow account + const err = bankkeeper.sendCoins(request.maker_address, escrowAddr, request.sell_token) + abortTransactionUnless(err == null) + // constraction IBC data packet + const packet = { + type: SwapMessageType.TYPE_MSG_MAKE_SWAP, + data: protobuf.encode(request), // encode the request message to protobuf bytes. + memo: "", + } + sendAtomicSwapPacket(packet) + + // create order and save order on Make chain. + const order = OrderBook.createOrder(msg) + //save order to store + store.save(order) } ``` ```ts -function takeSwap(request TakeSwap) { - +function takeSwap(request TakeSwapMsg) { + const order = OrderBook.findOrderById(request.order_id) + abortTransactionUnless(order != null) + abortTransactionUnless(order.expired_timestamp < Now().timestamp()) + abortTransactionUnless(order.maker.buy_token.denom === request.sell_token.denom) + abortTransactionUnless(order.maker.buy_token.amount === request.sell_token.amount) + + const balance = bank_keeper.getBalances(request.taker_address) + abortTransactionUnless(balance.amount > request.sell_token.Amount) + // found the escrow address by source port and source channel + const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) + // lock the sell_token to escrow account + const err = bankkeeper.sendCoins(request.taker_address, escrowAddr, request.sell_token) + abortTransactionUnless(err == null) + // constract IBC data packet + const packet = { + type: SwapMessageType.TYPE_MSG_TAKE_SWAP, + data: protobuf.encode(request), // encode the request message to protobuf bytes. + memo: "", + } + sendAtomicSwapPacket(packet) + + order.taker = request } ``` @@ -270,7 +325,6 @@ function onChanCloseConfirm(portIdentifier: Identifier, channelIdentifier: Ident ```typescript function sendAtomicSwapPacket(swapPacket AtomicSwapPacketData) { - // send packet using the interface defined in ICS4 handler.sendPacket( getCapability("port"), @@ -286,51 +340,41 @@ function sendAtomicSwapPacket(swapPacket AtomicSwapPacketData) { `onRecvPacket` is called by the routing module when a packet addressed to this module has been received. ```typescript -function onRecvPacket(data: AtomicSwapPacketData) { - switch data.Type { - case TYPE_MSG_MAKE_SWAP: - var msg types.MsgMakeSwapRequest - - if err := types.ModuleCdc.Unmarshal(data.Data, &msg); err != nil { - return err - } - if err := k.executeMakeSwap(ctx, packet, &msg); err != nil { - return err - } - - return nil - - case TYPE_MSG_TAKE_SWAP: - var msg types.MsgTakeSwapRequest - - if err := types.ModuleCdc.Unmarshal(data.Data, &msg); err != nil { - return err - } - if err2 := k.executeTakeSwap(ctx, packet, &msg); err2 != nil { - return err2 - } else { - return nil - } - - case TYPE_MSG_CANCEL_SWAP: - var msg types.MsgCancelSwapRequest - - if err := types.ModuleCdc.Unmarshal(data.Data, &msg); err != nil { - return err - } - if err2 := k.executeCancelSwap(ctx, packet, &msg); err2 != nil { - return err2 - } else { - return nil - } - - default: - return types.ErrUnknownDataPacket +function onRecvPacket(packet: AtomicSwapPacketData) { + switch packet.type { + case TYPE_MSG_MAKE_SWAP: + const make_msg = protobuf.decode(packet.bytes) + + // check if buy_token is native token on taker chain + const supply = bank_keeper.getSuppy(make_msg.buy_token.denom) + abortTransactionUnless(supply > 0) + + // create order and save order on taker chain. + const order = OrderBook.createOrder(msg) + order.status = Status.SYNC + //save order to store + store.save(order) + break; + case TYPE_MSG_TAKE_SWAP: + const take_msg = protobuf.decode(packet.bytes) + const order = OrderBook.findOrderById(take_msg.order_id) + abortTransactionUnless(order != null) + abortTransactionUnless(order.status == Status.SYNC) + abortTransactionUnless(order.expired_timestamp < Now().timestamp()) + abortTransactionUnless(take_msg.sell_token.denom == order.maker.buy_token.denom) + abortTransactionUnless(take_msg.sell_token.amount == order.maker.buy_token.amount) + + // send maker.sell_token to taker's receiving address + bank_keeper.sendCoins(escrowAddr, take_msg.taker_receiving_address, order.maker.sell_token) + + // update status of order + order.status = Status.COMPLETE + order.complete_timestamp = take_msg.create_timestamp + store.save(order) + break; + default: + throw new Error("ErrUnknownDataPacket") } - - ctx.EventManager().EmitTypedEvents(&data) - - return nil } ``` @@ -340,40 +384,35 @@ function onRecvPacket(data: AtomicSwapPacketData) { function onAcknowledgePacket( packet: AtomicSwapPacketData, acknowledgement: bytes) { -switch ack.Response.(type) { - case *channeltypes.Acknowledgement_Error: - return k.refundPacketToken(ctx, packet, data) - default: - switch data.Type { - case TYPE_MSG_TAKE_SWAP: - var msg types.MsgTakeSwapRequest - - if err := types.ModuleCdc.Unmarshal(data.Data, &msg); err != nil { - return err - } - // check order status - if order, ok := k.GetLimitOrder(ctx, msg.OrderId); ok { - k.executeTakeSwap(ctx, order, &msg, StepAcknowledgement) - } else { - return types.ErrOrderDoesNotExists - } - break - - case TYPE_MSG_CANCEL_SWAP: - var msg types.MsgCancelSwapRequest - - if err := types.ModuleCdc.Unmarshal(data.Data, &msg); err != nil { - return err - } - if err2 := k.executeCancel(ctx, &msg, StepAcknowledgement); err2 != nil { - return err2 - } else { - return nil - } - break - } + // ack is failed + if (!ack.success) { + refundToken(packet) + } else { + switch packet.type { + case TYPE_MSG_MAKE_SWAP: + const make_msg = protobuf.decode(packet.bytes) + + // create order and save order on taker chain. + const order = OrderBook.findOrderById(make_msg) + order.status = Status.SYNC + //save order to store + store.save(order) + break; + case TYPE_MSG_TAKE_SWAP: + const take_msg = protobuf.decode(packet.bytes) + + // create order and save order on taker chain. + const order = OrderBook.findOrderById(take_msg.order_id) + order.status = Status.COMPLETE + //save order to store + store.save(order) + + //send tokens to maker + bank_keeper.sendCoins(escrowAddr, order.maker.maker_receiving_address, take_msg.sell_token) + break; + default: + throw new Error("ErrUnknownDataPacket") } - return nil } ``` @@ -392,6 +431,16 @@ function onTimeoutPacket(packet: Packet) { function refundTokens(packet: Packet) { AtomicSwapPacketData data = packet.data //send tokens from module to message sender + switch packet.type { + case TYPE_MSG_MAKE_SWAP: + const msg = protobuf.decode(data) + bank_keeper.sendCoins(escrowAddr, msg.maker_address, msg.sell_token) + break; + case TYPE_MSG_TAKE_SWAP: + const msg = protobuf.decode(data) + bank_keeper.sendCoins(escrowAddr, msg.taker_address, msg.sell_token) + } + } } ``` From cf028bfdc37ab483caf7ef835b25e87764e29c42 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Tue, 8 Nov 2022 12:59:06 +0800 Subject: [PATCH 11/84] save taker to the order when it is filled --- spec/app/ics-100-atomic-swap/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index d1bb3f6f2..01a7a732f 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -369,6 +369,7 @@ function onRecvPacket(packet: AtomicSwapPacketData) { // update status of order order.status = Status.COMPLETE + order.taker = take_msg order.complete_timestamp = take_msg.create_timestamp store.save(order) break; @@ -404,7 +405,8 @@ function onAcknowledgePacket( // create order and save order on taker chain. const order = OrderBook.findOrderById(take_msg.order_id) order.status = Status.COMPLETE - //save order to store + order.taker = take_msg + order.complete_timestamp = take_msg.create_timestamp store.save(order) //send tokens to maker From 14983d87b231c61967047e0e977356a9d034d8c8 Mon Sep 17 00:00:00 2001 From: EddyG Date: Tue, 8 Nov 2022 16:56:21 +0700 Subject: [PATCH 12/84] polish language --- spec/app/ics-100-atomic-swap/README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 01a7a732f..01e2e04b5 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -83,13 +83,13 @@ interface MakeSwapMsg { // the tokens to be exchanged sell_token : Coin buy_token: Coin; - // the sender address + // the maker's address maker_address: string; - // the sender's address on the taker chain + // the maker's address on the taker chain maker_receiving_address string; // if desired_taker is specified, // only the desired_taker is allowed to take this order - // this is address on the taker chain + // this is the address on the taker chain desired_taker: string; create_timestamp: int64; expired_timestamp: int64; @@ -101,9 +101,9 @@ interface TakeSwapMsg { order_id: string; // the tokens to be sell sell_token: Coin; - // the sender address + // the taker's address taker_address: string; - // the sender's address on the taker chain + // the taker's address on the maker chain taker_receiving_address: string; create_timestamp: int64; } @@ -477,6 +477,8 @@ Aug 15, 2022 - Draft written Oct 6, 2022 - Draft revised +Nov 8, 2022 - Draft revised + ## Copyright All content herein is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). From f7bea6eae266c52d888cd0aeec0cd6521394358a Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Wed, 9 Nov 2022 09:46:52 +0800 Subject: [PATCH 13/84] change order status when refund --- spec/app/ics-100-atomic-swap/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 01a7a732f..3c274dce7 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -433,16 +433,23 @@ function onTimeoutPacket(packet: Packet) { function refundTokens(packet: Packet) { AtomicSwapPacketData data = packet.data //send tokens from module to message sender + cosnt order_id; switch packet.type { case TYPE_MSG_MAKE_SWAP: const msg = protobuf.decode(data) bank_keeper.sendCoins(escrowAddr, msg.maker_address, msg.sell_token) + order_id = generateOrderId(msg) break; case TYPE_MSG_TAKE_SWAP: const msg = protobuf.decode(data) bank_keeper.sendCoins(escrowAddr, msg.taker_address, msg.sell_token) + order = msg.order_id } } + // update order state to cancel + order = Orderbook.findOrderById(orderId) + order.status = Status.CANCEL + store.save(order) } ``` From f22ce34d72a04fbfae6831bee0404d8e92f4f9b2 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Fri, 11 Nov 2022 10:14:06 +0800 Subject: [PATCH 14/84] improve the cancel flow --- spec/app/ics-100-atomic-swap/README.md | 74 ++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 3c274dce7..218cda928 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -62,6 +62,7 @@ enum SwapMessageType { TYPE_UNSPECIFIED = 0, TYPE_MSG_MAKE_SWAP = 1, TYPE_MSG_TAKE_SWAP = 2, + TYPE_MSG_CANCEL_SWAP = 3, } // AtomicSwapPacketData is comprised of a swap message type, raw transaction and optional memo field. @@ -72,13 +73,13 @@ interface AtomicSwapPacketData { } ``` -All `AtomicSwapPacketData` will be forwarded to the corresponding message handler to execute according to its type. There are 2 types: +All `AtomicSwapPacketData` will be forwarded to the corresponding message handler to execute according to its type. There are 3 types: ```typescript interface MakeSwapMsg { - // the port on which the packet will be sent + // the port on which the packet will be sent, user sepecify when they create the order source_port string - // the channel by which the packet will be sent + // the channel by which the packet will be sent, user sepecify when they create the order source_channel: string; // the tokens to be exchanged sell_token : Coin @@ -109,6 +110,13 @@ interface TakeSwapMsg { } ``` +```typescript +interface TakeCancelMsg { + order_id: string; + maker_address: string; +} +``` + Both the maker chain and taker chain maintain separate orderbooks. Orders are saved in both maker chain and taker chain. ```typescript @@ -153,7 +161,7 @@ function generateOrderId(msg MakeSwapMsg) { 1. User creates an order on the maker chain with specified parameters (see type `MakeSwap`). Tokens are sent to the escrow address owned by the module. The order is saved on the maker chain 2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is also saved on the taker chain. 3. A packet is subsequently relayed back for acknowledgement. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. -4. An order should not be taken once the current time is later than expired timestamp, and all expired orders should refund its escrow tokens at `BeginBlock` function. +4. An order should not be taken once the current time is later than expired timestamp, and all expired orders should not allow to trade, but can be canceled. #### Taking a swap @@ -161,6 +169,13 @@ function generateOrderId(msg MakeSwapMsg) { 2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the destination address. 3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain is sent to the related destination address. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. +#### Cancelling a swap + +1. The maker cancels a previously created order. +2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the order is cancelled on the taker chain. if the make order has take order in-flight, the cancel order will be rejected. +3. A packet is relayed back where upon acknowledgement the order on the maker chain is also cancelled. +4. Only refund if the taker chain confirmed the cancel request + ### Sub-protocols The sub-protocols described herein should be implemented in a "Fungible Token Swap" module with access to a bank module and to the IBC routing module. @@ -196,6 +211,7 @@ function takeSwap(request TakeSwapMsg) { abortTransactionUnless(order.expired_timestamp < Now().timestamp()) abortTransactionUnless(order.maker.buy_token.denom === request.sell_token.denom) abortTransactionUnless(order.maker.buy_token.amount === request.sell_token.amount) + abortTransactionUnless(order.taker == null) const balance = bank_keeper.getBalances(request.taker_address) abortTransactionUnless(balance.amount > request.sell_token.Amount) @@ -212,7 +228,30 @@ function takeSwap(request TakeSwapMsg) { } sendAtomicSwapPacket(packet) - order.taker = request + //update state of order + order.taker = request // mark the order has been occupied + store.save(order) +} +``` + + +```ts +function cancelSwap(request TakeCancelMsg) { + const order = OrderBook.findOrderById(request.order_id) + // check if the order exists + abortTransactionUnless(order != null) + // make sure the sender is the owner of the order. + abortTransactionUnless(order.maker.maker_address == request.maker_address) + abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) + + // constract IBC data packet + const packet = { + type: SwapMessageType.TYPE_MSG_CANCEL_SWAP, + data: protobuf.encode(request), // encode the request message to protobuf bytes. + memo: "", + } + // the request is sent to the taker chain, and the taker chain decides if the cancel order is accepted or not + sendAtomicSwapPacket(packet) } ``` @@ -373,6 +412,18 @@ function onRecvPacket(packet: AtomicSwapPacketData) { order.complete_timestamp = take_msg.create_timestamp store.save(order) break; + case TYPE_MSG_CANCEL_SWAP: + const cancel_msg = protobuf.decode(packet.bytes) + const order = OrderBook.findOrderById(cancel_msg.order_id) + abortTransactionUnless(order != null) + abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) + abortTransactionUnless(order.taker != null) // the maker order has not been occupied + + // update status of order + order.status = Status.CANCEL + order.cancel_timestamp = cancel_msg.create_timestamp + store.save(order) + break; default: throw new Error("ErrUnknownDataPacket") } @@ -412,6 +463,19 @@ function onAcknowledgePacket( //send tokens to maker bank_keeper.sendCoins(escrowAddr, order.maker.maker_receiving_address, take_msg.sell_token) break; + case TYPE_MSG_CANCEL_SWAP: + const cancel_msg = protobuf.decode(packet.bytes) + + // create order and save order on taker chain. + const order = OrderBook.findOrderById(cancel_msg.order_id) + // update state on maker chain + order.status = Status.CANCEL + order.cancel_timestamp = cancel_msg.create_timestamp + store.save(order) + + //send tokens back to maker + bank_keeper.sendCoins(escrowAddr, order.maker.maker_address, order.maker.sell_token) + break; default: throw new Error("ErrUnknownDataPacket") } From 07873c24ab25ea4e6660de55aee0677b5c9dc7f2 Mon Sep 17 00:00:00 2001 From: EddyG Date: Fri, 11 Nov 2022 16:43:23 +0700 Subject: [PATCH 15/84] polish text --- spec/app/ics-100-atomic-swap/README.md | 54 +++++++++++++------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 78b8e6495..55ffaaf92 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -77,9 +77,9 @@ All `AtomicSwapPacketData` will be forwarded to the corresponding message handle ```typescript interface MakeSwapMsg { - // the port on which the packet will be sent, user sepecify when they create the order + // the port on which the packet will be sent, specified by the maker when the order is created source_port string - // the channel by which the packet will be sent, user sepecify when they create the order + // the channel by which the packet will be sent, specified by the maker when the order is created source_channel: string; // the tokens to be exchanged sell_token : Coin @@ -161,20 +161,18 @@ function generateOrderId(msg MakeSwapMsg) { 1. User creates an order on the maker chain with specified parameters (see type `MakeSwap`). Tokens are sent to the escrow address owned by the module. The order is saved on the maker chain 2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is also saved on the taker chain. 3. A packet is subsequently relayed back for acknowledgement. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. -4. An order should not be taken once the current time is later than expired timestamp, and all expired orders should not allow to trade, but can be canceled. #### Taking a swap -1. A user takes an order on the taker chain by triggering `TakeSwap`. Tokens are sent to the escrow address owned by the module. +1. A user takes an order on the taker chain by triggering `TakeSwap`. Tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expired_timestamp` 2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the destination address. 3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain is sent to the related destination address. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Cancelling a swap -1. The maker cancels a previously created order. -2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the order is cancelled on the taker chain. if the make order has take order in-flight, the cancel order will be rejected. -3. A packet is relayed back where upon acknowledgement the order on the maker chain is also cancelled. -4. Only refund if the taker chain confirmed the cancel request +1. The maker cancels a previously created order. Expired orders can also be cancelled. +2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is cancelled on the taker chain. If the order is in the process of being taken (a packet with `TakeSwapMsg` is being relayed from the taker chain to the maker chain), the cancellation will be rejected. +3. A packet is relayed back where upon acknowledgement the order on the maker chain is also cancelled. The refund only occurs if the taker chain confirmed the cancellation request. ### Sub-protocols @@ -184,12 +182,12 @@ The sub-protocols described herein should be implemented in a "Fungible Token Sw function makeSwap(request MakeSwapMsg) { const balance = bank_keeper.getBalances(request.make_address) abortTransactionUnless(balance.amount > request.sell_token.Amount) - // found the escrow address by source port and source channel + // gets escrow address by source port and source channel const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) - // lock the sell_token to escrow account + // locks the sell_token to the escrow account const err = bankkeeper.sendCoins(request.maker_address, escrowAddr, request.sell_token) abortTransactionUnless(err == null) - // constraction IBC data packet + // contructs the IBC data packet const packet = { type: SwapMessageType.TYPE_MSG_MAKE_SWAP, data: protobuf.encode(request), // encode the request message to protobuf bytes. @@ -197,9 +195,9 @@ function makeSwap(request MakeSwapMsg) { } sendAtomicSwapPacket(packet) - // create order and save order on Make chain. + // creates and saves order on the maker chain. const order = OrderBook.createOrder(msg) - //save order to store + //saves order to store store.save(order) } ``` @@ -215,12 +213,12 @@ function takeSwap(request TakeSwapMsg) { const balance = bank_keeper.getBalances(request.taker_address) abortTransactionUnless(balance.amount > request.sell_token.Amount) - // found the escrow address by source port and source channel + // gets the escrow address by source port and source channel const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) - // lock the sell_token to escrow account + // locks the sell_token to the escrow account const err = bankkeeper.sendCoins(request.taker_address, escrowAddr, request.sell_token) abortTransactionUnless(err == null) - // constract IBC data packet + // constructs the IBC data packet const packet = { type: SwapMessageType.TYPE_MSG_TAKE_SWAP, data: protobuf.encode(request), // encode the request message to protobuf bytes. @@ -228,8 +226,8 @@ function takeSwap(request TakeSwapMsg) { } sendAtomicSwapPacket(packet) - //update state of order - order.taker = request // mark the order has been occupied + //update order state + order.taker = request // mark that the order has been occupied store.save(order) } ``` @@ -238,13 +236,13 @@ function takeSwap(request TakeSwapMsg) { ```ts function cancelSwap(request TakeCancelMsg) { const order = OrderBook.findOrderById(request.order_id) - // check if the order exists + // checks if the order exists abortTransactionUnless(order != null) - // make sure the sender is the owner of the order. + // make sure the sender is the maker of the order. abortTransactionUnless(order.maker.maker_address == request.maker_address) abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) - // constract IBC data packet + // constructs the IBC data packet const packet = { type: SwapMessageType.TYPE_MSG_CANCEL_SWAP, data: protobuf.encode(request), // encode the request message to protobuf bytes. @@ -384,14 +382,14 @@ function onRecvPacket(packet: AtomicSwapPacketData) { case TYPE_MSG_MAKE_SWAP: const make_msg = protobuf.decode(packet.bytes) - // check if buy_token is native token on taker chain + // check if buy_token is native token on the taker chain const supply = bank_keeper.getSuppy(make_msg.buy_token.denom) abortTransactionUnless(supply > 0) - // create order and save order on taker chain. + // create and save order on the taker chain. const order = OrderBook.createOrder(msg) order.status = Status.SYNC - //save order to store + //saves order to store store.save(order) break; case TYPE_MSG_TAKE_SWAP: @@ -444,7 +442,7 @@ function onAcknowledgePacket( case TYPE_MSG_MAKE_SWAP: const make_msg = protobuf.decode(packet.bytes) - // create order and save order on taker chain. + // create and save order on the taker chain. const order = OrderBook.findOrderById(make_msg) order.status = Status.SYNC //save order to store @@ -453,7 +451,7 @@ function onAcknowledgePacket( case TYPE_MSG_TAKE_SWAP: const take_msg = protobuf.decode(packet.bytes) - // create order and save order on taker chain. + // create and save order on the taker chain. const order = OrderBook.findOrderById(take_msg.order_id) order.status = Status.COMPLETE order.taker = take_msg @@ -466,7 +464,7 @@ function onAcknowledgePacket( case TYPE_MSG_CANCEL_SWAP: const cancel_msg = protobuf.decode(packet.bytes) - // create order and save order on taker chain. + // create and save order on the taker chain. const order = OrderBook.findOrderById(cancel_msg.order_id) // update state on maker chain order.status = Status.CANCEL @@ -548,7 +546,7 @@ Aug 15, 2022 - Draft written Oct 6, 2022 - Draft revised -Nov 8, 2022 - Draft revised +Nov 11, 2022 - Draft revised ## Copyright From 879c30b1400a4ec6e34bc3cfdc1aa6208f32bf7c Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Sat, 12 Nov 2022 11:49:56 +0800 Subject: [PATCH 16/84] add source_port and source_channel --- spec/app/ics-100-atomic-swap/README.md | 27 ++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 55ffaaf92..2508728f8 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -94,6 +94,8 @@ interface MakeSwapMsg { desired_taker: string; create_timestamp: int64; expired_timestamp: int64; + timeout_height: int64, + timeout_timestamp: int64, } ``` @@ -107,6 +109,8 @@ interface TakeSwapMsg { // the taker's address on the maker chain taker_receiving_address: string; create_timestamp: int64; + timeout_height: int64, + timeout_timestamp: int64, } ``` @@ -114,6 +118,8 @@ interface TakeSwapMsg { interface TakeCancelMsg { order_id: string; maker_address: string; + timeout_height: int64, + timeout_timestamp: int64, } ``` @@ -131,7 +137,10 @@ interface OrderBook { id: string; maker: MakeSwapMsg; status: Status; - channel_id: string; + // set onRecieved(), Make sure that the take order can only be sent to the chain the make order came from + port_id_on_taker_chain: string; + // set onRecieved(), Make sure that the take order can only be sent to the chain the make order came from + channel_id_on_taker_chain: string; taker: TakeSwap; cancel_timestamp: int64; complete_timestamp: int64; @@ -193,7 +202,7 @@ function makeSwap(request MakeSwapMsg) { data: protobuf.encode(request), // encode the request message to protobuf bytes. memo: "", } - sendAtomicSwapPacket(packet) + sendAtomicSwapPacket(packet, request.source_port, request.source_channel, request.timeout_height, request.timeout_timestamp) // creates and saves order on the maker chain. const order = OrderBook.createOrder(msg) @@ -224,7 +233,7 @@ function takeSwap(request TakeSwapMsg) { data: protobuf.encode(request), // encode the request message to protobuf bytes. memo: "", } - sendAtomicSwapPacket(packet) + sendAtomicSwapPacket(packet, order.port_id_on_taker_chain, order.channel_id_on_taker_chain, request.timeout_height, request.timeout_timestamp) //update order state order.taker = request // mark that the order has been occupied @@ -249,7 +258,8 @@ function cancelSwap(request TakeCancelMsg) { memo: "", } // the request is sent to the taker chain, and the taker chain decides if the cancel order is accepted or not - sendAtomicSwapPacket(packet) + // the cancelation can only be sent to the same chain as the make order. + sendAtomicSwapPacket(packet, order.maker.source_port_id, order.maker.source_channel_id request.timeout_height, request.timeout_timestamp) } ``` @@ -361,7 +371,10 @@ function onChanCloseConfirm(portIdentifier: Identifier, channelIdentifier: Ident `sendAtomicSwapPacket` must be called by a transaction handler in the module which performs appropriate signature checks, specific to the account owner on the host state machine. ```typescript -function sendAtomicSwapPacket(swapPacket AtomicSwapPacketData) { +function sendAtomicSwapPacket( + swapPacket AtomicSwapPacketData, + sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp: int64 +) { // send packet using the interface defined in ICS4 handler.sendPacket( getCapability("port"), @@ -377,7 +390,7 @@ function sendAtomicSwapPacket(swapPacket AtomicSwapPacketData) { `onRecvPacket` is called by the routing module when a packet addressed to this module has been received. ```typescript -function onRecvPacket(packet: AtomicSwapPacketData) { +function onRecvPacket(packet channeltypes.Packet) { switch packet.type { case TYPE_MSG_MAKE_SWAP: const make_msg = protobuf.decode(packet.bytes) @@ -389,6 +402,8 @@ function onRecvPacket(packet: AtomicSwapPacketData) { // create and save order on the taker chain. const order = OrderBook.createOrder(msg) order.status = Status.SYNC + order.port_id_on_taker_chain = packet.destinationPort + order.channel_id_on_taker_chain = packet.destinationChannel //saves order to store store.save(order) break; From 38815945b6c90f61bfc01a3917ca278b282f099a Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Sun, 13 Nov 2022 09:49:56 +0800 Subject: [PATCH 17/84] fix incorrect comment in OnReceive and OnAcknowledgement --- spec/app/ics-100-atomic-swap/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 2508728f8..80250119d 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -457,7 +457,7 @@ function onAcknowledgePacket( case TYPE_MSG_MAKE_SWAP: const make_msg = protobuf.decode(packet.bytes) - // create and save order on the taker chain. + // update order status on the maker chain. const order = OrderBook.findOrderById(make_msg) order.status = Status.SYNC //save order to store @@ -466,7 +466,7 @@ function onAcknowledgePacket( case TYPE_MSG_TAKE_SWAP: const take_msg = protobuf.decode(packet.bytes) - // create and save order on the taker chain. + // update order status on the taker chain. const order = OrderBook.findOrderById(take_msg.order_id) order.status = Status.COMPLETE order.taker = take_msg @@ -479,7 +479,7 @@ function onAcknowledgePacket( case TYPE_MSG_CANCEL_SWAP: const cancel_msg = protobuf.decode(packet.bytes) - // create and save order on the taker chain. + // update order status on the maker chain. const order = OrderBook.findOrderById(cancel_msg.order_id) // update state on maker chain order.status = Status.CANCEL From 14603168cb32c28d4c15f5f88ed28b7db6d1d4e8 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:01:56 +0800 Subject: [PATCH 18/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 80250119d..445937c85 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -22,7 +22,7 @@ Users may wish to exchange tokens without transfering tokens away from its nativ `Atomic Swap`: An exchange of tokens from separate chains without transfering tokens from one blockchain to another. -`Order`: an offer to exchange quantity X of token A for quantity Y of token B. Tokens offered are sent to an escrow account (owned by the module) +`Order`: An offer to exchange quantity X of token A for quantity Y of token B. Tokens offered are sent to an escrow account (owned by the module). `Maker`: A user that makes or initiates an order. From 2bbc831099953a372f3cd05c77b75ecf98ab7921 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:02:11 +0800 Subject: [PATCH 19/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 445937c85..78dc2f36b 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -35,7 +35,7 @@ Users may wish to exchange tokens without transfering tokens away from its nativ ### Desired Properties - `Permissionless`: no need to whitelist connections, modules, or denominations. -- `Gaurantee of exchange`: no occurence of a user receiving tokens without the equivalent promised exchange. +- `Guarantee of exchange`: no occurence of a user receiving tokens without the equivalent promised exchange. - `Escrow enabled`: an account owned by the module will hold tokens and facilitate exchange. - `Refundable`: tokens are refunded by escrow when an order is cancelled - `Basic orderbook`: a store of orders functioning as an orderbook system From 1e0204fcb254627ea72210fa02ff03bff08f57a5 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:02:26 +0800 Subject: [PATCH 20/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 78dc2f36b..4346bc410 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -48,7 +48,7 @@ Users may wish to exchange tokens without transfering tokens away from its nativ A maker offers token A in exchange for token B by making an order. The order specifies the quantity and price of exchange, and sends the offered token A to the maker chain's escrow account. -Any taker on a different chain with token B can accept the offer by taking the order. The taker sends the desired amount of token B to the taker chain's escrow account. +Any taker on a different chain with token B can accept the offer by taking the order. The taker sends the desired amount of token B to the taker chain's escrow account. The escrow account on each respective chain transfers the corresponding token amounts to each user's receiving address, without requiring the usual ibc transfer. From 573bcdecb6cef14194ce54e0cbd39ccf28120bc1 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:03:43 +0800 Subject: [PATCH 21/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 4346bc410..ea7775160 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -68,7 +68,7 @@ enum SwapMessageType { // AtomicSwapPacketData is comprised of a swap message type, raw transaction and optional memo field. interface AtomicSwapPacketData { type: SwapMessageType; - data: types[]; + data: []byte; memo: string; } ``` From 551c386d0aa412726b1a2cdf28faf519237cd349 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:06:52 +0800 Subject: [PATCH 22/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index ea7775160..21193189e 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -78,7 +78,7 @@ All `AtomicSwapPacketData` will be forwarded to the corresponding message handle ```typescript interface MakeSwapMsg { // the port on which the packet will be sent, specified by the maker when the order is created - source_port string + source_port string; // the channel by which the packet will be sent, specified by the maker when the order is created source_channel: string; // the tokens to be exchanged From 6ce60d9171724064970da61c4d7dfc886b56c164 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:07:15 +0800 Subject: [PATCH 23/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 21193189e..03d0c4240 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -54,7 +54,7 @@ The escrow account on each respective chain transfers the corresponding token am ### Data Structures -Only one packet data type is required: `AtomicSwapPacketData`, which specifies the swap message type, data(protobuf marshalled) and a memo field. +Only one packet data type is required: `AtomicSwapPacketData`, which specifies the swap message type, data (protobuf marshalled) and a memo field. ```typescript enum SwapMessageType { From 2640dc50a4cda63fd19fe6564b09990a118d6832 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:07:51 +0800 Subject: [PATCH 24/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 03d0c4240..a1321f519 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -79,7 +79,7 @@ All `AtomicSwapPacketData` will be forwarded to the corresponding message handle interface MakeSwapMsg { // the port on which the packet will be sent, specified by the maker when the order is created source_port string; - // the channel by which the packet will be sent, specified by the maker when the order is created + // the channel on which the packet will be sent, specified by the maker when the order is created source_channel: string; // the tokens to be exchanged sell_token : Coin From f0554782dd930649781508d9197da6dcf429535e Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:08:13 +0800 Subject: [PATCH 25/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index a1321f519..157fa25d0 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -82,7 +82,7 @@ interface MakeSwapMsg { // the channel on which the packet will be sent, specified by the maker when the order is created source_channel: string; // the tokens to be exchanged - sell_token : Coin + sell_token : Coin; buy_token: Coin; // the maker's address maker_address: string; From 485bb98cd0f8c508e958547529c6646b37d1c6b1 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:08:42 +0800 Subject: [PATCH 26/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 157fa25d0..96c5de32b 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -92,7 +92,7 @@ interface MakeSwapMsg { // only the desired_taker is allowed to take this order // this is the address on the taker chain desired_taker: string; - create_timestamp: int64; + creation_timestamp: int64; expired_timestamp: int64; timeout_height: int64, timeout_timestamp: int64, From 860e21cb59c97c17e0d08ad9213f1eb610789867 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:09:14 +0800 Subject: [PATCH 27/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 96c5de32b..54cadc19b 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -189,7 +189,7 @@ The sub-protocols described herein should be implemented in a "Fungible Token Sw ```ts function makeSwap(request MakeSwapMsg) { - const balance = bank_keeper.getBalances(request.make_address) + const balance = bank.getBalances(request.make_address) abortTransactionUnless(balance.amount > request.sell_token.Amount) // gets escrow address by source port and source channel const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) From 8db2a7bf7fd35029b53166821e7e83fd7e33cebf Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:09:28 +0800 Subject: [PATCH 28/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 54cadc19b..ad835b3b3 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -93,7 +93,7 @@ interface MakeSwapMsg { // this is the address on the taker chain desired_taker: string; creation_timestamp: int64; - expired_timestamp: int64; + expiration_timestamp: int64; timeout_height: int64, timeout_timestamp: int64, } From 8230374d1d32959db1188997703967c13250b94a Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:10:27 +0800 Subject: [PATCH 29/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index ad835b3b3..9c415ee8a 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -205,7 +205,7 @@ function makeSwap(request MakeSwapMsg) { sendAtomicSwapPacket(packet, request.source_port, request.source_channel, request.timeout_height, request.timeout_timestamp) // creates and saves order on the maker chain. - const order = OrderBook.createOrder(msg) + const order = OrderBook.createOrder(request) //saves order to store store.save(order) } From d7e80c48031decf2ab86efb646c2394c72631e74 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Wed, 14 Dec 2022 18:44:20 +0800 Subject: [PATCH 30/84] Apply suggestions from code review Co-authored-by: Carlos Rodriguez --- spec/app/ics-100-atomic-swap/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 9c415ee8a..6413a41d3 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -215,7 +215,7 @@ function makeSwap(request MakeSwapMsg) { function takeSwap(request TakeSwapMsg) { const order = OrderBook.findOrderById(request.order_id) abortTransactionUnless(order != null) - abortTransactionUnless(order.expired_timestamp < Now().timestamp()) + abortTransactionUnless(order.expired_timestamp < currentTimestamp()) abortTransactionUnless(order.maker.buy_token.denom === request.sell_token.denom) abortTransactionUnless(order.maker.buy_token.amount === request.sell_token.amount) abortTransactionUnless(order.taker == null) @@ -259,7 +259,7 @@ function cancelSwap(request TakeCancelMsg) { } // the request is sent to the taker chain, and the taker chain decides if the cancel order is accepted or not // the cancelation can only be sent to the same chain as the make order. - sendAtomicSwapPacket(packet, order.maker.source_port_id, order.maker.source_channel_id request.timeout_height, request.timeout_timestamp) + sendAtomicSwapPacket(packet, order.maker.source_port, order.maker.source_channel request.timeout_height, request.timeout_timestamp) } ``` @@ -400,7 +400,7 @@ function onRecvPacket(packet channeltypes.Packet) { abortTransactionUnless(supply > 0) // create and save order on the taker chain. - const order = OrderBook.createOrder(msg) + const order = OrderBook.createOrder(make_msg) order.status = Status.SYNC order.port_id_on_taker_chain = packet.destinationPort order.channel_id_on_taker_chain = packet.destinationChannel @@ -412,7 +412,7 @@ function onRecvPacket(packet channeltypes.Packet) { const order = OrderBook.findOrderById(take_msg.order_id) abortTransactionUnless(order != null) abortTransactionUnless(order.status == Status.SYNC) - abortTransactionUnless(order.expired_timestamp < Now().timestamp()) + abortTransactionUnless(order.expired_timestamp < currentTimestamp()) abortTransactionUnless(take_msg.sell_token.denom == order.maker.buy_token.denom) abortTransactionUnless(take_msg.sell_token.amount == order.maker.buy_token.amount) @@ -458,7 +458,7 @@ function onAcknowledgePacket( const make_msg = protobuf.decode(packet.bytes) // update order status on the maker chain. - const order = OrderBook.findOrderById(make_msg) + const order = OrderBook.findOrder(generateOrderId(make_msg)) order.status = Status.SYNC //save order to store store.save(order) From 12fddfc1f730fbfeadcec9e0aaca08cc6e343a4d Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 15 Dec 2022 10:38:34 +0800 Subject: [PATCH 31/84] improve spec --- spec/app/ics-100-atomic-swap/README.md | 191 +++++++++++++---------- spec/app/ics-100-atomic-swap/ibcswap.png | 4 +- 2 files changed, 109 insertions(+), 86 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 6413a41d3..724e8cc1e 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -64,7 +64,9 @@ enum SwapMessageType { TYPE_MSG_TAKE_SWAP = 2, TYPE_MSG_CANCEL_SWAP = 3, } +``` +```typescript // AtomicSwapPacketData is comprised of a swap message type, raw transaction and optional memo field. interface AtomicSwapPacketData { type: SwapMessageType; @@ -78,48 +80,56 @@ All `AtomicSwapPacketData` will be forwarded to the corresponding message handle ```typescript interface MakeSwapMsg { // the port on which the packet will be sent, specified by the maker when the order is created - source_port string; + sourcePort string; // the channel on which the packet will be sent, specified by the maker when the order is created - source_channel: string; + sourceChannel: string; // the tokens to be exchanged - sell_token : Coin; - buy_token: Coin; + sellToken : Coin; + buyToken: Coin; // the maker's address - maker_address: string; + makerAddress: string; // the maker's address on the taker chain - maker_receiving_address string; - // if desired_taker is specified, - // only the desired_taker is allowed to take this order + makerReceivingAddress string; + // if desiredTaker is specified, + // only the desiredTaker is allowed to take this order // this is the address on the taker chain - desired_taker: string; - creation_timestamp: int64; - expiration_timestamp: int64; - timeout_height: int64, - timeout_timestamp: int64, + desiredTaker: string; + creationTimestamp: int64; + expirationTimestamp: int64; + timeoutHeight: int64, + timeoutTimestamp: int64, } ``` ```typescript interface TakeSwapMsg { - order_id: string; + orderId: string; // the tokens to be sell - sell_token: Coin; + sellToken: Coin; // the taker's address - taker_address: string; + takerAddress: string; // the taker's address on the maker chain - taker_receiving_address: string; - create_timestamp: int64; - timeout_height: int64, - timeout_timestamp: int64, + takerReceivingAddress: string; + creationTimestamp: int64; + timeoutHeight: int64, + timeoutTimestamp: int64, } ``` ```typescript interface TakeCancelMsg { - order_id: string; - maker_address: string; - timeout_height: int64, - timeout_timestamp: int64, + orderId: string; + makerAddress: string; + timeoutHeight: int64, + timeoutTimestamp: int64, +} +``` + +Note: Coin is sdk.Coin, +```ts +interface Coin { + amount: int64, + denom: string } ``` @@ -138,12 +148,12 @@ interface OrderBook { maker: MakeSwapMsg; status: Status; // set onRecieved(), Make sure that the take order can only be sent to the chain the make order came from - port_id_on_taker_chain: string; + portIdOnTakerChain: string; // set onRecieved(), Make sure that the take order can only be sent to the chain the make order came from - channel_id_on_taker_chain: string; + channelIdOnTakerChain: string; taker: TakeSwap; - cancel_timestamp: int64; - complete_timestamp: int64; + cancelTimestamp: int64; + completeTimestamp: int64; createOrder(msg: MakeSwapMsg) OrderBook { return { @@ -193,8 +203,8 @@ function makeSwap(request MakeSwapMsg) { abortTransactionUnless(balance.amount > request.sell_token.Amount) // gets escrow address by source port and source channel const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) - // locks the sell_token to the escrow account - const err = bankkeeper.sendCoins(request.maker_address, escrowAddr, request.sell_token) + // locks the sellToken to the escrow account + const err = bank.sendCoins(request.makerAddress, escrowAddr, request.sellToken) abortTransactionUnless(err == null) // contructs the IBC data packet const packet = { @@ -202,7 +212,7 @@ function makeSwap(request MakeSwapMsg) { data: protobuf.encode(request), // encode the request message to protobuf bytes. memo: "", } - sendAtomicSwapPacket(packet, request.source_port, request.source_channel, request.timeout_height, request.timeout_timestamp) + sendAtomicSwapPacket(packet, request.sourcePort, request.sourceChannel, request.timeoutHeight, request.timeoutTimestamp) // creates and saves order on the maker chain. const order = OrderBook.createOrder(request) @@ -213,19 +223,21 @@ function makeSwap(request MakeSwapMsg) { ```ts function takeSwap(request TakeSwapMsg) { - const order = OrderBook.findOrderById(request.order_id) + const order = store.findOrderById(request.orderId) abortTransactionUnless(order != null) - abortTransactionUnless(order.expired_timestamp < currentTimestamp()) - abortTransactionUnless(order.maker.buy_token.denom === request.sell_token.denom) - abortTransactionUnless(order.maker.buy_token.amount === request.sell_token.amount) + abortTransactionUnless(order.expirationTimestamp < currentTimestamp()) + abortTransactionUnless(order.maker.buyToken.denom === request.sellToken.denom) + abortTransactionUnless(order.maker.buyToken.amount === request.sellToken.amount) abortTransactionUnless(order.taker == null) + //if `desiredTaker` is set, only the desiredTaker can accept the order. + abortTransactionUnless(order.maker.desiredTaker != null && order.maker.desiredTaker != request.takerAddress) - const balance = bank_keeper.getBalances(request.taker_address) - abortTransactionUnless(balance.amount > request.sell_token.Amount) + const balance = bank.getBalances(request.takerAddress) + abortTransactionUnless(balance.amount > request.sellToken.amount) // gets the escrow address by source port and source channel const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) - // locks the sell_token to the escrow account - const err = bankkeeper.sendCoins(request.taker_address, escrowAddr, request.sell_token) + // locks the sellToken to the escrow account + const err = bank.sendCoins(request.takerAddress, escrowAddr, request.sellToken) abortTransactionUnless(err == null) // constructs the IBC data packet const packet = { @@ -233,7 +245,7 @@ function takeSwap(request TakeSwapMsg) { data: protobuf.encode(request), // encode the request message to protobuf bytes. memo: "", } - sendAtomicSwapPacket(packet, order.port_id_on_taker_chain, order.channel_id_on_taker_chain, request.timeout_height, request.timeout_timestamp) + sendAtomicSwapPacket(packet, order.portIdOnTakerChain, order.channelIdOnTakerChain, request.timeoutHeight, request.timeoutTimestamp) //update order state order.taker = request // mark that the order has been occupied @@ -244,11 +256,11 @@ function takeSwap(request TakeSwapMsg) { ```ts function cancelSwap(request TakeCancelMsg) { - const order = OrderBook.findOrderById(request.order_id) + const order = store.findOrderById(request.orderId) // checks if the order exists abortTransactionUnless(order != null) // make sure the sender is the maker of the order. - abortTransactionUnless(order.maker.maker_address == request.maker_address) + abortTransactionUnless(order.maker.makerAddress == request.makerAddress) abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) // constructs the IBC data packet @@ -259,7 +271,7 @@ function cancelSwap(request TakeCancelMsg) { } // the request is sent to the taker chain, and the taker chain decides if the cancel order is accepted or not // the cancelation can only be sent to the same chain as the make order. - sendAtomicSwapPacket(packet, order.maker.source_port, order.maker.source_channel request.timeout_height, request.timeout_timestamp) + sendAtomicSwapPacket(packet, order.maker.sourcePort, order.maker.sourceChannel request.timeoutHeight, request.timeoutTimestamp) } ``` @@ -329,7 +341,10 @@ function onChanOpenTry( abortTransactionUnless(order === UNORDERED) // assert that version is "ics100-1" abortTransactionUnless(counterpartyVersion === "ics100-1") - + + // allocate an escrow address + channelEscrowAddresses[channelIdentifier] = newAddress() + return "ics100-1", nil } ``` @@ -391,50 +406,52 @@ function sendAtomicSwapPacket( ```typescript function onRecvPacket(packet channeltypes.Packet) { - switch packet.type { + const swapPaket: AtomicSwapPacketData = packet.data + switch swapPaket.type { case TYPE_MSG_MAKE_SWAP: - const make_msg = protobuf.decode(packet.bytes) + const makeMsg = protobuf.decode(swapPaket.data) // check if buy_token is native token on the taker chain - const supply = bank_keeper.getSuppy(make_msg.buy_token.denom) + const supply = bank.getSuppy(makeMsg.buyToken.denom) abortTransactionUnless(supply > 0) // create and save order on the taker chain. - const order = OrderBook.createOrder(make_msg) + const order = OrderBook.createOrder(makeMsg) order.status = Status.SYNC - order.port_id_on_taker_chain = packet.destinationPort - order.channel_id_on_taker_chain = packet.destinationChannel + order.portIdOnTakerChain = packet.destinationPort + order.channelIdOnTakerChain = packet.destinationChannel //saves order to store store.save(order) break; case TYPE_MSG_TAKE_SWAP: - const take_msg = protobuf.decode(packet.bytes) - const order = OrderBook.findOrderById(take_msg.order_id) + const takeMsg = protobuf.decode(swapPaket.data) + const order = store.findOrderById(takeMsg.orderId) abortTransactionUnless(order != null) abortTransactionUnless(order.status == Status.SYNC) - abortTransactionUnless(order.expired_timestamp < currentTimestamp()) - abortTransactionUnless(take_msg.sell_token.denom == order.maker.buy_token.denom) - abortTransactionUnless(take_msg.sell_token.amount == order.maker.buy_token.amount) + abortTransactionUnless(order.expiredTimestamp < currentTimestamp()) + abortTransactionUnless(take_msg.sellToken.denom == order.maker.buyToken.denom) + abortTransactionUnless(take_msg.sellToken.amount == order.maker.buyToken.amount) - // send maker.sell_token to taker's receiving address - bank_keeper.sendCoins(escrowAddr, take_msg.taker_receiving_address, order.maker.sell_token) + const escrowAddr = escrowAddress(packet.destinationPort, packet.destinationChannel) + // send maker.sellToken to taker's receiving address + bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken) // update status of order order.status = Status.COMPLETE - order.taker = take_msg - order.complete_timestamp = take_msg.create_timestamp + order.taker = takeMsg + order.completeTimestamp = takeMsg.creationTimestamp store.save(order) break; case TYPE_MSG_CANCEL_SWAP: - const cancel_msg = protobuf.decode(packet.bytes) - const order = OrderBook.findOrderById(cancel_msg.order_id) + const cancelMsg = protobuf.decode(swapPaket.data) + const order = store.findOrderById(cancelMsg.orderId) abortTransactionUnless(order != null) abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) abortTransactionUnless(order.taker != null) // the maker order has not been occupied // update status of order order.status = Status.CANCEL - order.cancel_timestamp = cancel_msg.create_timestamp + order.cancelTimestamp = cancelMsg.creationTimestamp store.save(order) break; default: @@ -447,47 +464,51 @@ function onRecvPacket(packet channeltypes.Packet) { ```typescript function onAcknowledgePacket( - packet: AtomicSwapPacketData, + packet channeltypes.Packet acknowledgement: bytes) { // ack is failed if (!ack.success) { refundToken(packet) } else { - switch packet.type { + + const swapPaket: AtomicSwapPacketData = protobuf.decode(packet.data) + const escrowAddr = escrowAddress(packet.sourcePort, packet.sourceChannel) + + switch swapPaket.type { case TYPE_MSG_MAKE_SWAP: - const make_msg = protobuf.decode(packet.bytes) + const makeMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = OrderBook.findOrder(generateOrderId(make_msg)) + const order = store.findOrderById(generateOrderId(makeMsg)) order.status = Status.SYNC //save order to store store.save(order) break; case TYPE_MSG_TAKE_SWAP: - const take_msg = protobuf.decode(packet.bytes) + const takeMsg = protobuf.decode(swapPaket.data) // update order status on the taker chain. - const order = OrderBook.findOrderById(take_msg.order_id) + const order = store.findOrderById(takeMsg.orderId) order.status = Status.COMPLETE - order.taker = take_msg - order.complete_timestamp = take_msg.create_timestamp + order.taker = takeMsg + order.completeTimestamp = takeMsg.creationTimestamp store.save(order) //send tokens to maker - bank_keeper.sendCoins(escrowAddr, order.maker.maker_receiving_address, take_msg.sell_token) + bank.sendCoins(escrowAddr, order.maker.makerReceivingAddress, takeMsg.sellToken) break; case TYPE_MSG_CANCEL_SWAP: - const cancel_msg = protobuf.decode(packet.bytes) + const cancelMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = OrderBook.findOrderById(cancel_msg.order_id) + const order = store.findOrderById(cancelMsg.orderId) // update state on maker chain order.status = Status.CANCEL - order.cancel_timestamp = cancel_msg.create_timestamp + order.cancelTimestamp = cancelMsg.creationTimestamp store.save(order) //send tokens back to maker - bank_keeper.sendCoins(escrowAddr, order.maker.maker_address, order.maker.sell_token) + bank.sendCoins(escrowAddr, order.maker.makerAddress, order.maker.sellToken) break; default: throw new Error("ErrUnknownDataPacket") @@ -508,23 +529,25 @@ function onTimeoutPacket(packet: Packet) { ```typescript function refundTokens(packet: Packet) { - AtomicSwapPacketData data = packet.data + const swapPacket: AtomicSwapPacketData = protobuf.decode(packet.data) + const escrowAddr = escrowAddress(packet.sourcePort, packet.sourceChannel) + //send tokens from module to message sender - cosnt order_id; - switch packet.type { + let orderId; + switch swapPaket.type { case TYPE_MSG_MAKE_SWAP: - const msg = protobuf.decode(data) - bank_keeper.sendCoins(escrowAddr, msg.maker_address, msg.sell_token) + const msg = protobuf.decode(swapPacket.data) + bank_keeper.sendCoins(escrowAddr, msg.makerAddress, msg.sellToken) order_id = generateOrderId(msg) break; case TYPE_MSG_TAKE_SWAP: - const msg = protobuf.decode(data) - bank_keeper.sendCoins(escrowAddr, msg.taker_address, msg.sell_token) - order = msg.order_id + const msg = protobuf.decode(swapPacket.data) + bank_keeper.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken) + order = msg.orderId } } // update order state to cancel - order = Orderbook.findOrderById(orderId) + order = store.findOrderById(orderId) order.status = Status.CANCEL store.save(order) } diff --git a/spec/app/ics-100-atomic-swap/ibcswap.png b/spec/app/ics-100-atomic-swap/ibcswap.png index 51aa1cc53..1dcb63be6 100644 --- a/spec/app/ics-100-atomic-swap/ibcswap.png +++ b/spec/app/ics-100-atomic-swap/ibcswap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fbd697de572f600be7942adb553f897e3e2b92aa0066ccf23b5d331e17f9b2c -size 60791 +oid sha256:83afa3c07f945e43e26134495d0b6d17b20fe7f75a076439abdae4e93e434b60 +size 59602 From 445ff8caa311b2b62631793cbfb1d20ec0b382ab Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 15 Dec 2022 10:58:23 +0800 Subject: [PATCH 32/84] fix typo --- spec/app/ics-100-atomic-swap/README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 724e8cc1e..7695d5a3f 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -183,7 +183,7 @@ function generateOrderId(msg MakeSwapMsg) { #### Taking a swap -1. A user takes an order on the taker chain by triggering `TakeSwap`. Tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expired_timestamp` +1. A user takes an order on the taker chain by triggering `TakeSwap`. Tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expirationTimestamp` 2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the destination address. 3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain is sent to the related destination address. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. @@ -229,7 +229,7 @@ function takeSwap(request TakeSwapMsg) { abortTransactionUnless(order.maker.buyToken.denom === request.sellToken.denom) abortTransactionUnless(order.maker.buyToken.amount === request.sellToken.amount) abortTransactionUnless(order.taker == null) - //if `desiredTaker` is set, only the desiredTaker can accept the order. + // if `desiredTaker` is set, only the desiredTaker can accept the order. abortTransactionUnless(order.maker.desiredTaker != null && order.maker.desiredTaker != request.takerAddress) const balance = bank.getBalances(request.takerAddress) @@ -429,9 +429,11 @@ function onRecvPacket(packet channeltypes.Packet) { abortTransactionUnless(order != null) abortTransactionUnless(order.status == Status.SYNC) abortTransactionUnless(order.expiredTimestamp < currentTimestamp()) - abortTransactionUnless(take_msg.sellToken.denom == order.maker.buyToken.denom) - abortTransactionUnless(take_msg.sellToken.amount == order.maker.buyToken.amount) - + abortTransactionUnless(takeMsg.sellToken.denom == order.maker.buyToken.denom) + abortTransactionUnless(takeMsg.sellToken.amount == order.maker.buyToken.amount) + // if `desiredTaker` is set, only the desiredTaker can accept the order. + abortTransactionUnless(order.maker.desiredTaker != null && order.maker.desiredTaker != takeMsg.takerAddress) + const escrowAddr = escrowAddress(packet.destinationPort, packet.destinationChannel) // send maker.sellToken to taker's receiving address bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken) From d76308d541bf1b88485c75b6cd7423fef2ba646e Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Tue, 20 Dec 2022 08:28:17 +0800 Subject: [PATCH 33/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Aditya --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 7695d5a3f..ad4a2c107 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -412,7 +412,7 @@ function onRecvPacket(packet channeltypes.Packet) { const makeMsg = protobuf.decode(swapPaket.data) // check if buy_token is native token on the taker chain - const supply = bank.getSuppy(makeMsg.buyToken.denom) + const supply = bank.getSupply(makeMsg.buyToken.denom) abortTransactionUnless(supply > 0) // create and save order on the taker chain. From c58a6f07828c0b7e5ee7bc15779cd8189677354b Mon Sep 17 00:00:00 2001 From: EddyG Date: Mon, 19 Dec 2022 17:47:26 -0800 Subject: [PATCH 34/84] modify destination reference --- spec/app/ics-100-atomic-swap/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index ad4a2c107..0e727f4af 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -184,8 +184,8 @@ function generateOrderId(msg MakeSwapMsg) { #### Taking a swap 1. A user takes an order on the taker chain by triggering `TakeSwap`. Tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expirationTimestamp` -2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the destination address. -3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain is sent to the related destination address. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. +2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the taker address on the maker chain. +3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain is sent to to the maker address on the taker chain. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Cancelling a swap From 62b8b2b8c7958e52e2a5581ffd63ee6ab450369e Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Tue, 20 Dec 2022 10:23:13 +0800 Subject: [PATCH 35/84] return ack on receivePacket(), use channelId + orderId as store key for orders --- spec/app/ics-100-atomic-swap/README.md | 52 ++++++++++++++++++++------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index ad4a2c107..935f7a56e 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -74,6 +74,17 @@ interface AtomicSwapPacketData { memo: string; } ``` +```typescript +type AtomicSwapPacketAcknowledgement = AtomicSwapPacketAcknowledgementSuccess | AtomicSwapPacketAcknowledgementFailure +interface AtomicSwapPacketAcknowledgementSuccess { + // This is binary 0x01 base64 encoded + result: "AQ==" +} + +interface AtomicSwapPacketAcknowledgementFailure { + error: string +} +``` All `AtomicSwapPacketData` will be forwarded to the corresponding message handler to execute according to its type. There are 3 types: @@ -323,7 +334,10 @@ function onChanOpenInit( // if empty, we return the default transfer version to core IBC // as the version for this channel abortTransactionUnless(version === "ics100-1" || version === "") - + + // allocate an escrow address + channelEscrowAddresses[channelIdentifier] = newAddress() + return "ics100-1", nil } ``` @@ -402,16 +416,19 @@ function sendAtomicSwapPacket( } ``` -`onRecvPacket` is called by the routing module when a packet addressed to this module has been received. +`onRecvPacket` is called by the routing module when a packet addressed to this module has been received. ```typescript function onRecvPacket(packet channeltypes.Packet) { const swapPaket: AtomicSwapPacketData = packet.data + + AtomicSwapPacketAcknowledgement ack = new AtomicSwapPacketAcknowledgementSuccess() + switch swapPaket.type { case TYPE_MSG_MAKE_SWAP: const makeMsg = protobuf.decode(swapPaket.data) - // check if buy_token is native token on the taker chain + // check if buyToken is a valid token on the taker chain, could be either native or ibc token const supply = bank.getSupply(makeMsg.buyToken.denom) abortTransactionUnless(supply > 0) @@ -421,11 +438,14 @@ function onRecvPacket(packet channeltypes.Packet) { order.portIdOnTakerChain = packet.destinationPort order.channelIdOnTakerChain = packet.destinationChannel //saves order to store - store.save(order) + const err = store.save(order) + if(err != null) { + ack = new AtomicSwapPacketAcknowledgementFailure("Failed to save the order on taker chain") + } break; case TYPE_MSG_TAKE_SWAP: const takeMsg = protobuf.decode(swapPaket.data) - const order = store.findOrderById(takeMsg.orderId) + const order = store.findOrderById(packet.destinationChannel, takeMsg.orderId) // store key is channelId + orderId abortTransactionUnless(order != null) abortTransactionUnless(order.status == Status.SYNC) abortTransactionUnless(order.expiredTimestamp < currentTimestamp()) @@ -436,7 +456,10 @@ function onRecvPacket(packet channeltypes.Packet) { const escrowAddr = escrowAddress(packet.destinationPort, packet.destinationChannel) // send maker.sellToken to taker's receiving address - bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken) + const err = bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken) + if(err != null) { + ack = new AtomicSwapPacketAcknowledgementFailure("transfer coins failed") + } // update status of order order.status = Status.COMPLETE @@ -446,7 +469,7 @@ function onRecvPacket(packet channeltypes.Packet) { break; case TYPE_MSG_CANCEL_SWAP: const cancelMsg = protobuf.decode(swapPaket.data) - const order = store.findOrderById(cancelMsg.orderId) + const order = store.findOrderById(packet.destinationChannel, cancelMsg.orderId) abortTransactionUnless(order != null) abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) abortTransactionUnless(order.taker != null) // the maker order has not been occupied @@ -454,11 +477,16 @@ function onRecvPacket(packet channeltypes.Packet) { // update status of order order.status = Status.CANCEL order.cancelTimestamp = cancelMsg.creationTimestamp - store.save(order) + const err = store.save(order) + if(err != null) { + ack = new AtomicSwapPacketAcknowledgementFailure("failed to cancel order on taker chain") + } break; default: - throw new Error("ErrUnknownDataPacket") + ack = new AtomicSwapPacketAcknowledgementFailure("unknown data packet") } + + return ack } ``` @@ -481,7 +509,7 @@ function onAcknowledgePacket( const makeMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = store.findOrderById(generateOrderId(makeMsg)) + const order = store.findOrderById(packet.sourceChannel, generateOrderId(makeMsg)) order.status = Status.SYNC //save order to store store.save(order) @@ -490,7 +518,7 @@ function onAcknowledgePacket( const takeMsg = protobuf.decode(swapPaket.data) // update order status on the taker chain. - const order = store.findOrderById(takeMsg.orderId) + const order = store.findOrderById(packet.sourceChannel, takeMsg.orderId) order.status = Status.COMPLETE order.taker = takeMsg order.completeTimestamp = takeMsg.creationTimestamp @@ -503,7 +531,7 @@ function onAcknowledgePacket( const cancelMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = store.findOrderById(cancelMsg.orderId) + const order = store.findOrderById(packet.sourceChannel, cancelMsg.orderId) // update state on maker chain order.status = Status.CANCEL order.cancelTimestamp = cancelMsg.creationTimestamp From 695e81cf31e7674fb14dbc920769d6a4c45853bb Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Tue, 20 Dec 2022 10:27:23 +0800 Subject: [PATCH 36/84] add nonce in Maker Msg in order to make the order id unique --- spec/app/ics-100-atomic-swap/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 935f7a56e..e05537319 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -109,6 +109,7 @@ interface MakeSwapMsg { expirationTimestamp: int64; timeoutHeight: int64, timeoutTimestamp: int64, + nonce: string, // a random digit string. } ``` From 6be9eb5f0147f6a25baa2d793717ae948817ba5d Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Tue, 20 Dec 2022 11:18:54 +0800 Subject: [PATCH 37/84] add channel in messages --- spec/app/ics-100-atomic-swap/README.md | 39 +++++++++++++++++--------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index e05537319..65dce4483 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -90,9 +90,9 @@ All `AtomicSwapPacketData` will be forwarded to the corresponding message handle ```typescript interface MakeSwapMsg { - // the port on which the packet will be sent, specified by the maker when the order is created + // the port on which the packet will be sent, specified by the maker when the message is created sourcePort string; - // the channel on which the packet will be sent, specified by the maker when the order is created + // the channel on which the packet will be sent, specified by the maker when the message is created sourceChannel: string; // the tokens to be exchanged sellToken : Coin; @@ -109,12 +109,14 @@ interface MakeSwapMsg { expirationTimestamp: int64; timeoutHeight: int64, timeoutTimestamp: int64, - nonce: string, // a random digit string. + nonce: string, // a random 6 digits string. } ``` ```typescript interface TakeSwapMsg { + // the channel on which the packet will be sent, specified by the taker when the message is created + sourceChannel: string; orderId: string; // the tokens to be sell sellToken: Coin; @@ -130,6 +132,8 @@ interface TakeSwapMsg { ```typescript interface TakeCancelMsg { + // the channel on which the packet will be sent, specified by the maker when the message is created + sourceChannel: string; orderId: string; makerAddress: string; timeoutHeight: int64, @@ -235,7 +239,7 @@ function makeSwap(request MakeSwapMsg) { ```ts function takeSwap(request TakeSwapMsg) { - const order = store.findOrderById(request.orderId) + const order = store.findOrderById(request.sourceChannelId, request.orderId) abortTransactionUnless(order != null) abortTransactionUnless(order.expirationTimestamp < currentTimestamp()) abortTransactionUnless(order.maker.buyToken.denom === request.sellToken.denom) @@ -244,6 +248,9 @@ function takeSwap(request TakeSwapMsg) { // if `desiredTaker` is set, only the desiredTaker can accept the order. abortTransactionUnless(order.maker.desiredTaker != null && order.maker.desiredTaker != request.takerAddress) + // check if this take message send to the correct chain + abortTransactionUnless(order.channelIdOnTakerChain == request.sourceChannelId) + const balance = bank.getBalances(request.takerAddress) abortTransactionUnless(balance.amount > request.sellToken.amount) // gets the escrow address by source port and source channel @@ -257,6 +264,7 @@ function takeSwap(request TakeSwapMsg) { data: protobuf.encode(request), // encode the request message to protobuf bytes. memo: "", } + sendAtomicSwapPacket(packet, order.portIdOnTakerChain, order.channelIdOnTakerChain, request.timeoutHeight, request.timeoutTimestamp) //update order state @@ -268,13 +276,16 @@ function takeSwap(request TakeSwapMsg) { ```ts function cancelSwap(request TakeCancelMsg) { - const order = store.findOrderById(request.orderId) + const order = store.findOrderById(request.sourceChannelId, request.orderId) // checks if the order exists abortTransactionUnless(order != null) // make sure the sender is the maker of the order. abortTransactionUnless(order.maker.makerAddress == request.makerAddress) abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) + // check if this take message send to the correct chain + abortTransactionUnless(request.sourceChannelId == order.maker.sourceChannel) + // constructs the IBC data packet const packet = { type: SwapMessageType.TYPE_MSG_CANCEL_SWAP, @@ -438,7 +449,7 @@ function onRecvPacket(packet channeltypes.Packet) { order.status = Status.SYNC order.portIdOnTakerChain = packet.destinationPort order.channelIdOnTakerChain = packet.destinationChannel - //saves order to store + // saves order to store const err = store.save(order) if(err != null) { ack = new AtomicSwapPacketAcknowledgementFailure("Failed to save the order on taker chain") @@ -446,7 +457,7 @@ function onRecvPacket(packet channeltypes.Packet) { break; case TYPE_MSG_TAKE_SWAP: const takeMsg = protobuf.decode(swapPaket.data) - const order = store.findOrderById(packet.destinationChannel, takeMsg.orderId) // store key is channelId + orderId + const order = store.findOrderById(packet.destinationChannel, takeMsg.orderId) abortTransactionUnless(order != null) abortTransactionUnless(order.status == Status.SYNC) abortTransactionUnless(order.expiredTimestamp < currentTimestamp()) @@ -510,7 +521,7 @@ function onAcknowledgePacket( const makeMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = store.findOrderById(packet.sourceChannel, generateOrderId(makeMsg)) + const order = store.findOrderById(makeMsg.sourceChannelId, generateOrderId(makeMsg)) order.status = Status.SYNC //save order to store store.save(order) @@ -519,7 +530,7 @@ function onAcknowledgePacket( const takeMsg = protobuf.decode(swapPaket.data) // update order status on the taker chain. - const order = store.findOrderById(packet.sourceChannel, takeMsg.orderId) + const order = store.findOrderById(takeMsg.sourceChannelId,takeMsg.orderId) order.status = Status.COMPLETE order.taker = takeMsg order.completeTimestamp = takeMsg.creationTimestamp @@ -532,7 +543,7 @@ function onAcknowledgePacket( const cancelMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = store.findOrderById(packet.sourceChannel, cancelMsg.orderId) + const order = store.findOrderById(cannelMsg.sourceChannelId, cancelMsg.orderId) // update state on maker chain order.status = Status.CANCEL order.cancelTimestamp = cancelMsg.creationTimestamp @@ -568,17 +579,17 @@ function refundTokens(packet: Packet) { switch swapPaket.type { case TYPE_MSG_MAKE_SWAP: const msg = protobuf.decode(swapPacket.data) - bank_keeper.sendCoins(escrowAddr, msg.makerAddress, msg.sellToken) - order_id = generateOrderId(msg) + bank.sendCoins(escrowAddr, msg.makerAddress, msg.sellToken) + orderId = generateOrderId(msg) break; case TYPE_MSG_TAKE_SWAP: const msg = protobuf.decode(swapPacket.data) - bank_keeper.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken) + bank.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken) order = msg.orderId } } // update order state to cancel - order = store.findOrderById(orderId) + order = store.findOrderById(packet.sourceChannel, orderId) order.status = Status.CANCEL store.save(order) } From fd964ac356bc6dded056033d8393346deacb9e64 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Tue, 20 Dec 2022 11:22:59 +0800 Subject: [PATCH 38/84] refactor the channel name --- spec/app/ics-100-atomic-swap/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 65dce4483..534cdb23c 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -239,7 +239,7 @@ function makeSwap(request MakeSwapMsg) { ```ts function takeSwap(request TakeSwapMsg) { - const order = store.findOrderById(request.sourceChannelId, request.orderId) + const order = store.findOrderById(request.sourceChannel, request.orderId) abortTransactionUnless(order != null) abortTransactionUnless(order.expirationTimestamp < currentTimestamp()) abortTransactionUnless(order.maker.buyToken.denom === request.sellToken.denom) @@ -276,7 +276,7 @@ function takeSwap(request TakeSwapMsg) { ```ts function cancelSwap(request TakeCancelMsg) { - const order = store.findOrderById(request.sourceChannelId, request.orderId) + const order = store.findOrderById(request.sourceChannel, request.orderId) // checks if the order exists abortTransactionUnless(order != null) // make sure the sender is the maker of the order. @@ -521,7 +521,7 @@ function onAcknowledgePacket( const makeMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = store.findOrderById(makeMsg.sourceChannelId, generateOrderId(makeMsg)) + const order = store.findOrderById(makeMsg.sourceChannel, generateOrderId(makeMsg)) order.status = Status.SYNC //save order to store store.save(order) @@ -530,7 +530,7 @@ function onAcknowledgePacket( const takeMsg = protobuf.decode(swapPaket.data) // update order status on the taker chain. - const order = store.findOrderById(takeMsg.sourceChannelId,takeMsg.orderId) + const order = store.findOrderById(takeMsg.sourceChannel, takeMsg.orderId) order.status = Status.COMPLETE order.taker = takeMsg order.completeTimestamp = takeMsg.creationTimestamp @@ -543,7 +543,7 @@ function onAcknowledgePacket( const cancelMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = store.findOrderById(cannelMsg.sourceChannelId, cancelMsg.orderId) + const order = store.findOrderById(cannelMsg.sourceChannel, cancelMsg.orderId) // update state on maker chain order.status = Status.CANCEL order.cancelTimestamp = cancelMsg.creationTimestamp From f92686a7b22f203bdd51fddb006ae8adeebc23c0 Mon Sep 17 00:00:00 2001 From: EddyG Date: Tue, 20 Dec 2022 06:49:16 -0800 Subject: [PATCH 39/84] additional info on cancels and timeouts --- spec/app/ics-100-atomic-swap/README.md | 29 +++++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 0e727f4af..96abf8da1 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -32,13 +32,22 @@ Users may wish to exchange tokens without transfering tokens away from its nativ `Taker Chain`: The blockchain where a taker takes or responds to an order. +`Maker Tokens`: Tokens a maker escrows to exchange for another token. + +`Taker Tokens`: Tokens a taker escrows to exchange for another token. + ### Desired Properties - `Permissionless`: no need to whitelist connections, modules, or denominations. - `Guarantee of exchange`: no occurence of a user receiving tokens without the equivalent promised exchange. - `Escrow enabled`: an account owned by the module will hold tokens and facilitate exchange. -- `Refundable`: tokens are refunded by escrow when an order is cancelled -- `Basic orderbook`: a store of orders functioning as an orderbook system +- `Refundable`: tokens are refunded by escrow when an order is cancelled, expired, or when the relevant packet timeout occurs. +- `Order cancellation`: orders without takers can be cancelled. +- `Order expiration`: all orders have an expiration time. +- `Basic orderbook`: a store of orders functioning as an orderbook system. + + + ## Technical Specification @@ -52,6 +61,10 @@ Any taker on a different chain with token B can accept the offer by taking the o The escrow account on each respective chain transfers the corresponding token amounts to each user's receiving address, without requiring the usual ibc transfer. +An order without takers can be cancelled. This enables users to rectify mistakes, such as inputting an incorrect price or taker address. Upon cancellation escrowed tokens will be refunded. + +In addition, all orders are required to have an customizable expiration time. Expired orders will also have its escrowed tokens refunded. + ### Data Structures Only one packet data type is required: `AtomicSwapPacketData`, which specifies the swap message type, data (protobuf marshalled) and a memo field. @@ -117,7 +130,7 @@ interface TakeSwapMsg { ``` ```typescript -interface TakeCancelMsg { +interface CancelSwapMsg { orderId: string; makerAddress: string; timeoutHeight: int64, @@ -177,19 +190,19 @@ function generateOrderId(msg MakeSwapMsg) { #### Making a swap -1. User creates an order on the maker chain with specified parameters (see type `MakeSwap`). Tokens are sent to the escrow address owned by the module. The order is saved on the maker chain +1. A maker creates an order on the maker chain with specified parameters (see type `MakeSwap`). Maker tokens are sent to the escrow address owned by the module. The order is saved on the maker chain 2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is also saved on the taker chain. 3. A packet is subsequently relayed back for acknowledgement. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Taking a swap -1. A user takes an order on the taker chain by triggering `TakeSwap`. Tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expirationTimestamp` +1. A taker takes an order on the taker chain by triggering `TakeSwap`. Taker tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expirationTimestamp` 2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the taker address on the maker chain. 3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain is sent to to the maker address on the taker chain. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Cancelling a swap -1. The maker cancels a previously created order. Expired orders can also be cancelled. +1. A maker cancels a previously created order. Expired orders can also be cancelled. 2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is cancelled on the taker chain. If the order is in the process of being taken (a packet with `TakeSwapMsg` is being relayed from the taker chain to the maker chain), the cancellation will be rejected. 3. A packet is relayed back where upon acknowledgement the order on the maker chain is also cancelled. The refund only occurs if the taker chain confirmed the cancellation request. @@ -255,7 +268,7 @@ function takeSwap(request TakeSwapMsg) { ```ts -function cancelSwap(request TakeCancelMsg) { +function cancelSwap(request CancelSwapMsg) { const order = store.findOrderById(request.orderId) // checks if the order exists abortTransactionUnless(order != null) @@ -269,7 +282,7 @@ function cancelSwap(request TakeCancelMsg) { data: protobuf.encode(request), // encode the request message to protobuf bytes. memo: "", } - // the request is sent to the taker chain, and the taker chain decides if the cancel order is accepted or not + // the request is sent to the taker chain, and the taker chain decides if the cancell order is accepted or not // the cancelation can only be sent to the same chain as the make order. sendAtomicSwapPacket(packet, order.maker.sourcePort, order.maker.sourceChannel request.timeoutHeight, request.timeoutTimestamp) } From 8405957d61cd6ae182169451f8a1dd0c90e9f262 Mon Sep 17 00:00:00 2001 From: EddyG Date: Tue, 20 Dec 2022 06:49:29 -0800 Subject: [PATCH 40/84] additional info on cancels and timeouts --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 96abf8da1..c91cc6df1 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -41,7 +41,7 @@ Users may wish to exchange tokens without transfering tokens away from its nativ - `Permissionless`: no need to whitelist connections, modules, or denominations. - `Guarantee of exchange`: no occurence of a user receiving tokens without the equivalent promised exchange. - `Escrow enabled`: an account owned by the module will hold tokens and facilitate exchange. -- `Refundable`: tokens are refunded by escrow when an order is cancelled, expired, or when the relevant packet timeout occurs. +- `Refundable`: tokens are refunded by escrow when an order is cancelled, expired, or when a timeout occurs. - `Order cancellation`: orders without takers can be cancelled. - `Order expiration`: all orders have an expiration time. - `Basic orderbook`: a store of orders functioning as an orderbook system. From c087b815d3fdc2e86b005ad4aae98bd4239188be Mon Sep 17 00:00:00 2001 From: EddyG Date: Tue, 20 Dec 2022 06:55:18 -0800 Subject: [PATCH 41/84] additional info on cancels and timeouts --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 296935825..f808b10b4 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -63,7 +63,7 @@ The escrow account on each respective chain transfers the corresponding token am An order without takers can be cancelled. This enables users to rectify mistakes, such as inputting an incorrect price or taker address. Upon cancellation escrowed tokens will be refunded. -In addition, all orders are required to have an customizable expiration time. Expired orders will also have its escrowed tokens refunded. +In addition, all orders are required to have an expiration time. Expired orders will also have its escrowed tokens refunded. This expiration time is customizeable. ### Data Structures From 596f8e7e15bfe71b9f631f5d653399c9769fd30e Mon Sep 17 00:00:00 2001 From: EddyG Date: Tue, 20 Dec 2022 06:56:17 -0800 Subject: [PATCH 42/84] additional info on cancels and timeouts --- spec/app/ics-100-atomic-swap/README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index f808b10b4..0de669efe 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -55,11 +55,7 @@ Users may wish to exchange tokens without transfering tokens away from its nativ -A maker offers token A in exchange for token B by making an order. The order specifies the quantity and price of exchange, and sends the offered token A to the maker chain's escrow account. - -Any taker on a different chain with token B can accept the offer by taking the order. The taker sends the desired amount of token B to the taker chain's escrow account. - -The escrow account on each respective chain transfers the corresponding token amounts to each user's receiving address, without requiring the usual ibc transfer. +A maker offers token A in exchange for token B by making an order. The order specifies the quantity and price of exchange, and sends the offered token A to the maker chain's escrow account. Any taker on a different chain with token B can accept the offer by taking the order. The taker sends the desired amount of token B to the taker chain's escrow account. The escrow account on each respective chain transfers the corresponding token amounts to each user's receiving address, without requiring the usual ibc transfer. An order without takers can be cancelled. This enables users to rectify mistakes, such as inputting an incorrect price or taker address. Upon cancellation escrowed tokens will be refunded. From f34854a48a3674c4c197ed8694a67319b56b96c2 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Thu, 22 Dec 2022 01:08:40 +0100 Subject: [PATCH 43/84] formatting style and consistency fixes --- spec/app/ics-100-atomic-swap/README.md | 329 +++++++++++++------------ 1 file changed, 166 insertions(+), 163 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index a1a6a5f2c..348fa8da5 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -26,7 +26,7 @@ Users may wish to exchange tokens without transfering tokens away from its nativ `Maker`: A user that makes or initiates an order. -`Taker`: Is the counterparty who takes or responds to an order. +`Taker`: The counterparty who takes or responds to an order. `Maker Chain`: The blockchain where a maker makes or initiaties an order. @@ -37,8 +37,8 @@ Users may wish to exchange tokens without transfering tokens away from its nativ - `Permissionless`: no need to whitelist connections, modules, or denominations. - `Guarantee of exchange`: no occurence of a user receiving tokens without the equivalent promised exchange. - `Escrow enabled`: an account owned by the module will hold tokens and facilitate exchange. -- `Refundable`: tokens are refunded by escrow when an order is cancelled -- `Basic orderbook`: a store of orders functioning as an orderbook system +- `Refundable`: tokens are refunded by escrow when an order is cancelled. +- `Basic orderbook`: a store of orders functioning as an orderbook system. ## Technical Specification @@ -50,7 +50,7 @@ A maker offers token A in exchange for token B by making an order. The order spe Any taker on a different chain with token B can accept the offer by taking the order. The taker sends the desired amount of token B to the taker chain's escrow account. -The escrow account on each respective chain transfers the corresponding token amounts to each user's receiving address, without requiring the usual ibc transfer. +The escrow account on each respective chain transfers the corresponding token amounts to each user's receiving address, without requiring the usual IBC transfer. ### Data Structures @@ -69,9 +69,9 @@ enum SwapMessageType { ```typescript // AtomicSwapPacketData is comprised of a swap message type, raw transaction and optional memo field. interface AtomicSwapPacketData { - type: SwapMessageType; - data: []byte; - memo: string; + type: SwapMessageType + data: []byte + memo: string } ``` ```typescript @@ -82,7 +82,7 @@ interface AtomicSwapPacketAcknowledgementSuccess { } interface AtomicSwapPacketAcknowledgementFailure { - error: string + error: string } ``` @@ -91,42 +91,42 @@ All `AtomicSwapPacketData` will be forwarded to the corresponding message handle ```typescript interface MakeSwapMsg { // the port on which the packet will be sent, specified by the maker when the message is created - sourcePort string; + sourcePort string // the channel on which the packet will be sent, specified by the maker when the message is created - sourceChannel: string; + sourceChannel: string // the tokens to be exchanged - sellToken : Coin; - buyToken: Coin; + sellToken : Coin + buyToken: Coin // the maker's address - makerAddress: string; + makerAddress: string // the maker's address on the taker chain - makerReceivingAddress string; + makerReceivingAddress string // if desiredTaker is specified, // only the desiredTaker is allowed to take this order // this is the address on the taker chain - desiredTaker: string; - creationTimestamp: int64; - expirationTimestamp: int64; - timeoutHeight: int64, - timeoutTimestamp: int64, - nonce: string, // a random 6 digits string. + desiredTaker: string + creationTimestamp: int64 + expirationTimestamp: int64 + timeoutHeight: int64 + timeoutTimestamp: int64 + nonce: string // a random 6 digits string } ``` ```typescript interface TakeSwapMsg { // the channel on which the packet will be sent, specified by the taker when the message is created - sourceChannel: string; - orderId: string; - // the tokens to be sell - sellToken: Coin; + sourceChannel: string + orderId: string + // the tokens to be sold + sellToken: Coin // the taker's address - takerAddress: string; + takerAddress: string // the taker's address on the maker chain - takerReceivingAddress: string; - creationTimestamp: int64; - timeoutHeight: int64, - timeoutTimestamp: int64, + takerReceivingAddress: string + creationTimestamp: int64 + timeoutHeight: int64 + timeoutTimestamp: int64 } ``` @@ -141,7 +141,7 @@ interface TakeCancelMsg { } ``` -Note: Coin is sdk.Coin, +Note: `Coin` is `sdk.Coin`, ```ts interface Coin { amount: int64, @@ -163,9 +163,9 @@ interface OrderBook { id: string; maker: MakeSwapMsg; status: Status; - // set onRecieved(), Make sure that the take order can only be sent to the chain the make order came from + // set onReceived(), Make sure that the take order can only be sent to the chain the make order came from portIdOnTakerChain: string; - // set onRecieved(), Make sure that the take order can only be sent to the chain the make order came from + // set onReceived(), Make sure that the take order can only be sent to the chain the make order came from channelIdOnTakerChain: string; taker: TakeSwap; cancelTimestamp: int64; @@ -173,9 +173,9 @@ interface OrderBook { createOrder(msg: MakeSwapMsg) OrderBook { return { - id : generateOrderId(msg) - status: Status.INITIAL - maker: msg, + id : generateOrderId(msg), + status: Status.INITIAL, + maker: msg } } } @@ -193,7 +193,7 @@ function generateOrderId(msg MakeSwapMsg) { #### Making a swap -1. User creates an order on the maker chain with specified parameters (see type `MakeSwap`). Tokens are sent to the escrow address owned by the module. The order is saved on the maker chain +1. User creates an order on the maker chain with specified parameters (see type `MakeSwap`). Tokens are sent to the escrow address owned by the module. The order is saved on the maker chain. 2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is also saved on the taker chain. 3. A packet is subsequently relayed back for acknowledgement. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. @@ -201,7 +201,7 @@ function generateOrderId(msg MakeSwapMsg) { 1. A user takes an order on the taker chain by triggering `TakeSwap`. Tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expirationTimestamp` 2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the taker address on the maker chain. -3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain is sent to to the maker address on the taker chain. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. +3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain are sent to to the maker address on the taker chain. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Cancelling a swap @@ -215,18 +215,18 @@ The sub-protocols described herein should be implemented in a "Fungible Token Sw ```ts function makeSwap(request MakeSwapMsg) { - const balance = bank.getBalances(request.make_address) - abortTransactionUnless(balance.amount > request.sell_token.Amount) + const balance = bank.getBalances(request.makerAddress) + abortTransactionUnless(balance.amount > request.sellToken.Amount) // gets escrow address by source port and source channel const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) // locks the sellToken to the escrow account const err = bank.sendCoins(request.makerAddress, escrowAddr, request.sellToken) - abortTransactionUnless(err == null) + abortTransactionUnless(err === null) // contructs the IBC data packet const packet = { type: SwapMessageType.TYPE_MSG_MAKE_SWAP, - data: protobuf.encode(request), // encode the request message to protobuf bytes. - memo: "", + data: protobuf.encode(request), // encode the request message to protobuf bytes + memo: "" } sendAtomicSwapPacket(packet, request.sourcePort, request.sourceChannel, request.timeoutHeight, request.timeoutTimestamp) @@ -239,68 +239,68 @@ function makeSwap(request MakeSwapMsg) { ```ts function takeSwap(request TakeSwapMsg) { - const order = store.findOrderById(request.sourceChannel, request.orderId) - abortTransactionUnless(order != null) - abortTransactionUnless(order.expirationTimestamp < currentTimestamp()) - abortTransactionUnless(order.maker.buyToken.denom === request.sellToken.denom) - abortTransactionUnless(order.maker.buyToken.amount === request.sellToken.amount) - abortTransactionUnless(order.taker == null) - // if `desiredTaker` is set, only the desiredTaker can accept the order. - abortTransactionUnless(order.maker.desiredTaker != null && order.maker.desiredTaker != request.takerAddress) + const order = store.findOrderById(request.sourceChannel, request.orderId) + abortTransactionUnless(order !== null) + abortTransactionUnless(order.expirationTimestamp < currentTimestamp()) + abortTransactionUnless(order.maker.buyToken.denom === request.sellToken.denom) + abortTransactionUnless(order.maker.buyToken.amount === request.sellToken.amount) + abortTransactionUnless(order.taker === null) + // if `desiredTaker` is set, only the desiredTaker can accept the order. + abortTransactionUnless(order.maker.desiredTaker !== null && order.maker.desiredTaker !== request.takerAddress) - // check if this take message send to the correct chain - abortTransactionUnless(order.channelIdOnTakerChain == request.sourceChannelId) + // check if this take message send to the correct chain + abortTransactionUnless(order.channelIdOnTakerChain === request.sourceChannel) - const balance = bank.getBalances(request.takerAddress) - abortTransactionUnless(balance.amount > request.sellToken.amount) - // gets the escrow address by source port and source channel - const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) - // locks the sellToken to the escrow account - const err = bank.sendCoins(request.takerAddress, escrowAddr, request.sellToken) - abortTransactionUnless(err == null) - // constructs the IBC data packet - const packet = { - type: SwapMessageType.TYPE_MSG_TAKE_SWAP, - data: protobuf.encode(request), // encode the request message to protobuf bytes. - memo: "", - } + const balance = bank.getBalances(request.takerAddress) + abortTransactionUnless(balance.amount > request.sellToken.amount) + // gets the escrow address by source port and source channel + const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) + // locks the sellToken to the escrow account + const err = bank.sendCoins(request.takerAddress, escrowAddr, request.sellToken) + abortTransactionUnless(err === null) + // constructs the IBC data packet + const packet = { + type: SwapMessageType.TYPE_MSG_TAKE_SWAP, + data: protobuf.encode(request), // encode the request message to protobuf bytes. + memo: "" + } - sendAtomicSwapPacket(packet, order.portIdOnTakerChain, order.channelIdOnTakerChain, request.timeoutHeight, request.timeoutTimestamp) + sendAtomicSwapPacket(packet, order.portIdOnTakerChain, order.channelIdOnTakerChain, request.timeoutHeight, request.timeoutTimestamp) - //update order state - order.taker = request // mark that the order has been occupied - store.save(order) + //update order state + order.taker = request // mark that the order has been occupied + store.save(order) } ``` ```ts function cancelSwap(request TakeCancelMsg) { - const order = store.findOrderById(request.sourceChannel, request.orderId) - // checks if the order exists - abortTransactionUnless(order != null) - // make sure the sender is the maker of the order. - abortTransactionUnless(order.maker.makerAddress == request.makerAddress) - abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) + const order = store.findOrderById(request.sourceChannel, request.orderId) + // checks if the order exists + abortTransactionUnless(order != null) + // make sure the sender is the maker of the order. + abortTransactionUnless(order.maker.makerAddress == request.makerAddress) + abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) - // check if this take message send to the correct chain - abortTransactionUnless(request.sourceChannelId == order.maker.sourceChannel) + // check if this take message send to the correct chain + abortTransactionUnless(request.sourceChannel == order.maker.sourceChannel) - // constructs the IBC data packet - const packet = { - type: SwapMessageType.TYPE_MSG_CANCEL_SWAP, - data: protobuf.encode(request), // encode the request message to protobuf bytes. - memo: "", - } - // the request is sent to the taker chain, and the taker chain decides if the cancel order is accepted or not - // the cancelation can only be sent to the same chain as the make order. - sendAtomicSwapPacket(packet, order.maker.sourcePort, order.maker.sourceChannel request.timeoutHeight, request.timeoutTimestamp) + // constructs the IBC data packet + const packet = { + type: SwapMessageType.TYPE_MSG_CANCEL_SWAP, + data: protobuf.encode(request), // encode the request message to protobuf bytes. + memo: "", + } + // the request is sent to the taker chain, and the taker chain decides if the cancel order is accepted or not + // the cancelation can only be sent to the same chain as the make order. + sendAtomicSwapPacket(packet, order.maker.sourcePort, order.maker.sourceChannel request.timeoutHeight, request.timeoutTimestamp) } ``` #### Port & channel setup -The fungible token swap module on a chain must always bind to a port with the id `atomicswap` +The fungible token swap module on a chain must always bind to a port with the id `atomicswap`. The `setup` function must be called exactly once when the module is created (perhaps when the blockchain itself is initialised) to bind to the appropriate port and create an escrow address (owned by the module). @@ -413,18 +413,21 @@ function onChanCloseConfirm(portIdentifier: Identifier, channelIdentifier: Ident ```typescript function sendAtomicSwapPacket( - swapPacket AtomicSwapPacketData, - sourcePort, sourceChannel, timeoutHeight, timeoutTimestamp: int64 + swapPacket: AtomicSwapPacketData, + sourcePort: string, + sourceChannel: string, + timeoutHeight: Height, + timeoutTimestamp: uint64 ) { - // send packet using the interface defined in ICS4 - handler.sendPacket( - getCapability("port"), - sourcePort, - sourceChannel, - timeoutHeight, - timeoutTimestamp, - swapPacket.getBytes(), // Should be proto marshalled bytes. - ) + // send packet using the interface defined in ICS4 + handler.sendPacket( + getCapability("port"), + sourcePort, + sourceChannel, + timeoutHeight, + timeoutTimestamp, + swapPacket.getBytes() // Should be proto marshalled bytes. + ) } ``` @@ -437,65 +440,65 @@ function onRecvPacket(packet channeltypes.Packet) { AtomicSwapPacketAcknowledgement ack = new AtomicSwapPacketAcknowledgementSuccess() switch swapPaket.type { - case TYPE_MSG_MAKE_SWAP: - const makeMsg = protobuf.decode(swapPaket.data) + case TYPE_MSG_MAKE_SWAP: + const makeMsg = protobuf.decode(swapPaket.data) - // check if buyToken is a valid token on the taker chain, could be either native or ibc token - const supply = bank.getSupply(makeMsg.buyToken.denom) - abortTransactionUnless(supply > 0) + // check if buyToken is a valid token on the taker chain, could be either native or ibc token + const supply = bank.getSupply(makeMsg.buyToken.denom) + abortTransactionUnless(supply > 0) - // create and save order on the taker chain. - const order = OrderBook.createOrder(makeMsg) - order.status = Status.SYNC - order.portIdOnTakerChain = packet.destinationPort - order.channelIdOnTakerChain = packet.destinationChannel - // saves order to store - const err = store.save(order) - if(err != null) { - ack = new AtomicSwapPacketAcknowledgementFailure("Failed to save the order on taker chain") - } - break; - case TYPE_MSG_TAKE_SWAP: - const takeMsg = protobuf.decode(swapPaket.data) - const order = store.findOrderById(packet.destinationChannel, takeMsg.orderId) - abortTransactionUnless(order != null) - abortTransactionUnless(order.status == Status.SYNC) - abortTransactionUnless(order.expiredTimestamp < currentTimestamp()) - abortTransactionUnless(takeMsg.sellToken.denom == order.maker.buyToken.denom) - abortTransactionUnless(takeMsg.sellToken.amount == order.maker.buyToken.amount) - // if `desiredTaker` is set, only the desiredTaker can accept the order. - abortTransactionUnless(order.maker.desiredTaker != null && order.maker.desiredTaker != takeMsg.takerAddress) + // create and save order on the taker chain. + const order = OrderBook.createOrder(makeMsg) + order.status = Status.SYNC + order.portIdOnTakerChain = packet.destinationPort + order.channelIdOnTakerChain = packet.destinationChannel + // saves order to store + const err = store.save(order) + if(err != null) { + ack = new AtomicSwapPacketAcknowledgementFailure("Failed to save the order on taker chain") + } + break; + case TYPE_MSG_TAKE_SWAP: + const takeMsg = protobuf.decode(swapPaket.data) + const order = store.findOrderById(packet.destinationChannel, takeMsg.orderId) + abortTransactionUnless(order !== null) + abortTransactionUnless(order.status === Status.SYNC) + abortTransactionUnless(order.expiredTimestamp < currentTimestamp()) + abortTransactionUnless(takeMsg.sellToken.denom === order.maker.buyToken.denom) + abortTransactionUnless(takeMsg.sellToken.amount === order.maker.buyToken.amount) + // if `desiredTaker` is set, only the desiredTaker can accept the order. + abortTransactionUnless(order.maker.desiredTaker !== null && order.maker.desiredTaker !== takeMsg.takerAddress) - const escrowAddr = escrowAddress(packet.destinationPort, packet.destinationChannel) - // send maker.sellToken to taker's receiving address - const err = bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken) - if(err != null) { - ack = new AtomicSwapPacketAcknowledgementFailure("transfer coins failed") - } + const escrowAddr = escrowAddress(packet.destinationPort, packet.destinationChannel) + // send maker.sellToken to taker's receiving address + const err = bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken) + if (err != null) { + ack = new AtomicSwapPacketAcknowledgementFailure("transfer coins failed") + } - // update status of order - order.status = Status.COMPLETE - order.taker = takeMsg - order.completeTimestamp = takeMsg.creationTimestamp - store.save(order) - break; - case TYPE_MSG_CANCEL_SWAP: - const cancelMsg = protobuf.decode(swapPaket.data) - const order = store.findOrderById(packet.destinationChannel, cancelMsg.orderId) - abortTransactionUnless(order != null) - abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) - abortTransactionUnless(order.taker != null) // the maker order has not been occupied + // update status of order + order.status = Status.COMPLETE + order.taker = takeMsg + order.completeTimestamp = takeMsg.creationTimestamp + store.save(order) + break; + case TYPE_MSG_CANCEL_SWAP: + const cancelMsg = protobuf.decode(swapPaket.data) + const order = store.findOrderById(packet.destinationChannel, cancelMsg.orderId) + abortTransactionUnless(order !== null) + abortTransactionUnless(order.status === Status.SYNC || order.status == Status.INITIAL) + abortTransactionUnless(order.taker !== null) // the maker order has not been occupied - // update status of order - order.status = Status.CANCEL - order.cancelTimestamp = cancelMsg.creationTimestamp - const err = store.save(order) - if(err != null) { - ack = new AtomicSwapPacketAcknowledgementFailure("failed to cancel order on taker chain") - } - break; - default: - ack = new AtomicSwapPacketAcknowledgementFailure("unknown data packet") + // update status of order + order.status = Status.CANCEL + order.cancelTimestamp = cancelMsg.creationTimestamp + const err = store.save(order) + if (err != null) { + ack = new AtomicSwapPacketAcknowledgementFailure("failed to cancel order on taker chain") + } + break; + default: + ack = new AtomicSwapPacketAcknowledgementFailure("unknown data packet") } return ack @@ -536,7 +539,7 @@ function onAcknowledgePacket( order.completeTimestamp = takeMsg.creationTimestamp store.save(order) - //send tokens to maker + // send tokens to maker bank.sendCoins(escrowAddr, order.maker.makerReceivingAddress, takeMsg.sellToken) break; case TYPE_MSG_CANCEL_SWAP: @@ -554,6 +557,7 @@ function onAcknowledgePacket( break; default: throw new Error("ErrUnknownDataPacket") + } } } ``` @@ -577,16 +581,15 @@ function refundTokens(packet: Packet) { //send tokens from module to message sender let orderId; switch swapPaket.type { - case TYPE_MSG_MAKE_SWAP: - const msg = protobuf.decode(swapPacket.data) - bank.sendCoins(escrowAddr, msg.makerAddress, msg.sellToken) - orderId = generateOrderId(msg) - break; - case TYPE_MSG_TAKE_SWAP: - const msg = protobuf.decode(swapPacket.data) - bank.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken) - order = msg.orderId - } + case TYPE_MSG_MAKE_SWAP: + const msg = protobuf.decode(swapPacket.data) + bank.sendCoins(escrowAddr, msg.makerAddress, msg.sellToken) + orderId = generateOrderId(msg) + break; + case TYPE_MSG_TAKE_SWAP: + const msg = protobuf.decode(swapPacket.data) + bank.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken) + order = msg.orderId } // update order state to cancel order = store.findOrderById(packet.sourceChannel, orderId) From c3e1ac5f1d6ed60a7ded6420b939ebc7dd4b4000 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Thu, 22 Dec 2022 01:10:33 +0100 Subject: [PATCH 44/84] fix alignment --- spec/app/ics-100-atomic-swap/README.md | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 348fa8da5..97580d0e0 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -215,25 +215,25 @@ The sub-protocols described herein should be implemented in a "Fungible Token Sw ```ts function makeSwap(request MakeSwapMsg) { - const balance = bank.getBalances(request.makerAddress) - abortTransactionUnless(balance.amount > request.sellToken.Amount) - // gets escrow address by source port and source channel - const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) - // locks the sellToken to the escrow account - const err = bank.sendCoins(request.makerAddress, escrowAddr, request.sellToken) - abortTransactionUnless(err === null) - // contructs the IBC data packet - const packet = { - type: SwapMessageType.TYPE_MSG_MAKE_SWAP, - data: protobuf.encode(request), // encode the request message to protobuf bytes - memo: "" - } - sendAtomicSwapPacket(packet, request.sourcePort, request.sourceChannel, request.timeoutHeight, request.timeoutTimestamp) + const balance = bank.getBalances(request.makerAddress) + abortTransactionUnless(balance.amount > request.sellToken.Amount) + // gets escrow address by source port and source channel + const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) + // locks the sellToken to the escrow account + const err = bank.sendCoins(request.makerAddress, escrowAddr, request.sellToken) + abortTransactionUnless(err === null) + // contructs the IBC data packet + const packet = { + type: SwapMessageType.TYPE_MSG_MAKE_SWAP, + data: protobuf.encode(request), // encode the request message to protobuf bytes + memo: "" + } + sendAtomicSwapPacket(packet, request.sourcePort, request.sourceChannel, request.timeoutHeight, request.timeoutTimestamp) - // creates and saves order on the maker chain. - const order = OrderBook.createOrder(request) - //saves order to store - store.save(order) + // creates and saves order on the maker chain. + const order = OrderBook.createOrder(request) + //saves order to store + store.save(order) } ``` From c960fc879fa3e1b7d5224835581d7f54c3f934bf Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Thu, 22 Dec 2022 01:14:35 +0100 Subject: [PATCH 45/84] more style fixes --- spec/app/ics-100-atomic-swap/README.md | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 97580d0e0..e09317630 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -133,18 +133,18 @@ interface TakeSwapMsg { ```typescript interface TakeCancelMsg { // the channel on which the packet will be sent, specified by the maker when the message is created - sourceChannel: string; - orderId: string; - makerAddress: string; - timeoutHeight: int64, - timeoutTimestamp: int64, + sourceChannel: string + orderId: string + makerAddress: string + timeoutHeight: int64 + timeoutTimestamp: int64 } ``` Note: `Coin` is `sdk.Coin`, ```ts interface Coin { - amount: int64, + amount: int64 denom: string } ``` @@ -161,15 +161,15 @@ enum Status { interface OrderBook { id: string; - maker: MakeSwapMsg; - status: Status; + maker: MakeSwapMsg + status: Status // set onReceived(), Make sure that the take order can only be sent to the chain the make order came from - portIdOnTakerChain: string; + portIdOnTakerChain: string // set onReceived(), Make sure that the take order can only be sent to the chain the make order came from - channelIdOnTakerChain: string; - taker: TakeSwap; - cancelTimestamp: int64; - completeTimestamp: int64; + channelIdOnTakerChain: string + taker: TakeSwap + cancelTimestamp: int64 + completeTimestamp: int64 createOrder(msg: MakeSwapMsg) OrderBook { return { @@ -182,8 +182,8 @@ interface OrderBook { // Order id is a global unique string function generateOrderId(msg MakeSwapMsg) { - cosnt bytes = protobuf.encode(msg) - return sha265(bytes) + cosnt bytes = protobuf.encode(msg) + return sha265(bytes) } From 4bc78998fbd7b2b6a4397c5d858f64925138ea34 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 22 Dec 2022 09:17:18 +0800 Subject: [PATCH 46/84] fixed portId issue on takeMsg --- spec/app/ics-100-atomic-swap/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index e09317630..027342710 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -254,7 +254,7 @@ function takeSwap(request TakeSwapMsg) { const balance = bank.getBalances(request.takerAddress) abortTransactionUnless(balance.amount > request.sellToken.amount) // gets the escrow address by source port and source channel - const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) + const escrowAddr = escrowAddress(order.portIdOnTakerChain, request.sourceChannel) // locks the sellToken to the escrow account const err = bank.sendCoins(request.takerAddress, escrowAddr, request.sellToken) abortTransactionUnless(err === null) @@ -469,7 +469,7 @@ function onRecvPacket(packet channeltypes.Packet) { // if `desiredTaker` is set, only the desiredTaker can accept the order. abortTransactionUnless(order.maker.desiredTaker !== null && order.maker.desiredTaker !== takeMsg.takerAddress) - const escrowAddr = escrowAddress(packet.destinationPort, packet.destinationChannel) + const escrowAddr = escrowAddress(order.portIdOnTakerChain, packet.destinationChannel) // send maker.sellToken to taker's receiving address const err = bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken) if (err != null) { From 9f6b3fbaeef74eb58579982860146685b0d08f95 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 22 Dec 2022 09:25:49 +0800 Subject: [PATCH 47/84] use both portId and channelId field in the order --- spec/app/ics-100-atomic-swap/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 027342710..82f877455 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -254,7 +254,7 @@ function takeSwap(request TakeSwapMsg) { const balance = bank.getBalances(request.takerAddress) abortTransactionUnless(balance.amount > request.sellToken.amount) // gets the escrow address by source port and source channel - const escrowAddr = escrowAddress(order.portIdOnTakerChain, request.sourceChannel) + const escrowAddr = escrowAddress(order.portIdOnTakerChain, order.channelIdOnTakerChain) // locks the sellToken to the escrow account const err = bank.sendCoins(request.takerAddress, escrowAddr, request.sellToken) abortTransactionUnless(err === null) @@ -469,7 +469,7 @@ function onRecvPacket(packet channeltypes.Packet) { // if `desiredTaker` is set, only the desiredTaker can accept the order. abortTransactionUnless(order.maker.desiredTaker !== null && order.maker.desiredTaker !== takeMsg.takerAddress) - const escrowAddr = escrowAddress(order.portIdOnTakerChain, packet.destinationChannel) + const escrowAddr = escrowAddress(order.portIdOnTakerChain, order.channelIdOnTakerChain) // send maker.sellToken to taker's receiving address const err = bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken) if (err != null) { From ebbf7c83d7b373588a1b76a80a6754991f65437a Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 22 Dec 2022 19:17:41 +0800 Subject: [PATCH 48/84] refund if orderId is not empty --- spec/app/ics-100-atomic-swap/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 82f877455..de25071fe 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -591,10 +591,13 @@ function refundTokens(packet: Packet) { bank.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken) order = msg.orderId } + // update order state to cancel - order = store.findOrderById(packet.sourceChannel, orderId) - order.status = Status.CANCEL - store.save(order) + if (orderId) { + order = store.findOrderById(packet.sourceChannel, orderId) + order.status = Status.CANCEL + store.save(order) + } } ``` From 870643670d9656ec248224a68bf071875ce9689f Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 22 Dec 2022 19:20:29 +0800 Subject: [PATCH 49/84] fix typo --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index dae623c76..7b144a8af 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -595,7 +595,7 @@ function refundTokens(packet: Packet) { case TYPE_MSG_TAKE_SWAP: const msg = protobuf.decode(swapPacket.data) bank.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken) - order = msg.orderId + orderId = msg.orderId } // update order state to cancel From aaa17dd16ba21dda9cc3de9a9ac394f849ef9c8a Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 22 Dec 2022 19:38:27 +0800 Subject: [PATCH 50/84] change order expiration to message expiration. --- spec/app/ics-100-atomic-swap/README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 7b144a8af..6ee22e27f 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -32,10 +32,6 @@ Users may wish to exchange tokens without transfering tokens away from its nativ `Taker Chain`: The blockchain where a taker takes or responds to an order. -`Maker Tokens`: Tokens a maker escrows to exchange for another token. - -`Taker Tokens`: Tokens a taker escrows to exchange for another token. - ### Desired Properties - `Permissionless`: no need to whitelist connections, modules, or denominations. @@ -43,7 +39,7 @@ Users may wish to exchange tokens without transfering tokens away from its nativ - `Escrow enabled`: an account owned by the module will hold tokens and facilitate exchange. - `Refundable`: tokens are refunded by escrow when an order is cancelled, expired, or when a timeout occurs. - `Order cancellation`: orders without takers can be cancelled. -- `Order expiration`: all orders have an expiration time. +- `Message expiration`: all messages have an expiration time. - `Basic orderbook`: a store of orders functioning as an orderbook system. ## Technical Specification @@ -56,7 +52,7 @@ A maker offers token A in exchange for token B by making an order. The order spe An order without takers can be cancelled. This enables users to rectify mistakes, such as inputting an incorrect price or taker address. Upon cancellation escrowed tokens will be refunded. -In addition, all orders are required to have an expiration time. Expired orders will also have its escrowed tokens refunded. This expiration time is customizeable. +In addition, all messages are required to have an expiration time. Expired messages will also have its escrowed tokens refunded. This expiration time is customizable. ### Data Structures @@ -199,13 +195,13 @@ function generateOrderId(msg MakeSwapMsg) { #### Making a swap -1. A maker creates an order on the maker chain with specified parameters (see type `MakeSwap`). Maker tokens are sent to the escrow address owned by the module. The order is saved on the maker chain +1. A maker creates an order on the maker chain with specified parameters (see type `MakeSwap`). Maker's sell tokens are sent to the escrow address owned by the module. The order is saved on the maker chain 2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is also saved on the taker chain. 3. A packet is subsequently relayed back for acknowledgement. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Taking a swap -1. A taker takes an order on the taker chain by triggering `TakeSwap`. Taker tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expirationTimestamp` +1. A taker takes an order on the taker chain by triggering `TakeSwap`. Taker's sell tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expirationTimestamp` 2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the taker address on the maker chain. 3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain are sent to to the maker address on the taker chain. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. From 9c54999f62a60a089963479a4c65451c75c3fa50 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 22 Dec 2022 20:31:24 +0800 Subject: [PATCH 51/84] change typo --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 6ee22e27f..0f190a1d8 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -169,7 +169,7 @@ interface OrderBook { portIdOnTakerChain: string // set onReceived(), Make sure that the take order can only be sent to the chain the make order came from channelIdOnTakerChain: string - taker: TakeSwap + taker: TakeSwapMsg cancelTimestamp: int64 completeTimestamp: int64 From 0adfbb1400a32bb8fe5d7d187f408dc0857f4d8a Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Fri, 23 Dec 2022 09:08:04 +0800 Subject: [PATCH 52/84] improve the refund --- spec/app/ics-100-atomic-swap/README.md | 34 ++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 0f190a1d8..8a2e13744 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -195,13 +195,13 @@ function generateOrderId(msg MakeSwapMsg) { #### Making a swap -1. A maker creates an order on the maker chain with specified parameters (see type `MakeSwap`). Maker's sell tokens are sent to the escrow address owned by the module. The order is saved on the maker chain +1. A maker creates an order on the maker chain with specified parameters (see type `MakeSwap`). The maker's sell tokens are sent to the escrow address owned by the module. The order is saved on the maker chain 2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is also saved on the taker chain. 3. A packet is subsequently relayed back for acknowledgement. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Taking a swap -1. A taker takes an order on the taker chain by triggering `TakeSwap`. Taker's sell tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expirationTimestamp` +1. A taker takes an order on the taker chain by triggering `TakeSwap`. The taker's sell tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expirationTimestamp` 2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the taker address on the maker chain. 3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain are sent to to the maker address on the taker chain. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. @@ -536,26 +536,30 @@ function onAcknowledgePacket( // update order status on the taker chain. const order = store.findOrderById(takeMsg.sourceChannel, takeMsg.orderId) + + // send tokens to maker + bank.sendCoins(escrowAddr, order.maker.makerReceivingAddress, takeMsg.sellToken) + order.status = Status.COMPLETE order.taker = takeMsg order.completeTimestamp = takeMsg.creationTimestamp store.save(order) - // send tokens to maker - bank.sendCoins(escrowAddr, order.maker.makerReceivingAddress, takeMsg.sellToken) break; case TYPE_MSG_CANCEL_SWAP: const cancelMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. const order = store.findOrderById(cannelMsg.sourceChannel, cancelMsg.orderId) + + // send tokens back to maker + bank.sendCoins(escrowAddr, order.maker.makerAddress, order.maker.sellToken) + // update state on maker chain order.status = Status.CANCEL order.cancelTimestamp = cancelMsg.creationTimestamp store.save(order) - //send tokens back to maker - bank.sendCoins(escrowAddr, order.maker.makerAddress, order.maker.sellToken) break; default: throw new Error("ErrUnknownDataPacket") @@ -581,24 +585,24 @@ function refundTokens(packet: Packet) { const escrowAddr = escrowAddress(packet.sourcePort, packet.sourceChannel) //send tokens from module to message sender - let orderId; switch swapPaket.type { case TYPE_MSG_MAKE_SWAP: const msg = protobuf.decode(swapPacket.data) bank.sendCoins(escrowAddr, msg.makerAddress, msg.sellToken) - orderId = generateOrderId(msg) + const orderId = generateOrderId(msg) + const order = store.findOrderById(packet.sourceChannel, orderId) + order.status = Status.CANCEL + store.save(order) break; case TYPE_MSG_TAKE_SWAP: const msg = protobuf.decode(swapPacket.data) bank.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken) - orderId = msg.orderId - } - - // update order state to cancel - if (orderId) { - order = store.findOrderById(packet.sourceChannel, orderId) - order.status = Status.CANCEL + const order = store.findOrderById(packet.sourceChannel, msg.orderId) + order.taker = null // release the occupation store.save(order) + break; + case TYPE_MSG_CANCEL_SWAP: + // do nothing, only send tokens back when cancel msg is acknowledged. } } ``` From 5690e86c2fade08a04c4015b3cf981c219a9a212 Mon Sep 17 00:00:00 2001 From: EddyG Date: Thu, 22 Dec 2022 19:10:56 -0800 Subject: [PATCH 53/84] Clarify regarding timeout window --- spec/app/ics-100-atomic-swap/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 8a2e13744..440dc2063 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -37,9 +37,8 @@ Users may wish to exchange tokens without transfering tokens away from its nativ - `Permissionless`: no need to whitelist connections, modules, or denominations. - `Guarantee of exchange`: no occurence of a user receiving tokens without the equivalent promised exchange. - `Escrow enabled`: an account owned by the module will hold tokens and facilitate exchange. -- `Refundable`: tokens are refunded by escrow when an order is cancelled, expired, or when a timeout occurs. +- `Refundable`: tokens are refunded by escrow when a timeout occurs, or when an order is cancelled. - `Order cancellation`: orders without takers can be cancelled. -- `Message expiration`: all messages have an expiration time. - `Basic orderbook`: a store of orders functioning as an orderbook system. ## Technical Specification @@ -52,7 +51,7 @@ A maker offers token A in exchange for token B by making an order. The order spe An order without takers can be cancelled. This enables users to rectify mistakes, such as inputting an incorrect price or taker address. Upon cancellation escrowed tokens will be refunded. -In addition, all messages are required to have an expiration time. Expired messages will also have its escrowed tokens refunded. This expiration time is customizable. +When making or taking an order, a timeout window is specified in the relayed data packet. A timeout will result in escrowed tokens refunded back. This timeout window is customizeable. ### Data Structures From 562f8f068512419facb5a426cf8194ac6072e047 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Tue, 10 Jan 2023 04:32:46 +0100 Subject: [PATCH 54/84] changed timestamp types to unit64 + fixed some nits --- spec/app/ics-100-atomic-swap/README.md | 92 +++++++++++++------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 440dc2063..4cd01e62e 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -51,7 +51,7 @@ A maker offers token A in exchange for token B by making an order. The order spe An order without takers can be cancelled. This enables users to rectify mistakes, such as inputting an incorrect price or taker address. Upon cancellation escrowed tokens will be refunded. -When making or taking an order, a timeout window is specified in the relayed data packet. A timeout will result in escrowed tokens refunded back. This timeout window is customizeable. +When making or taking an order, a timeout window is specified in the relayed data packet. A timeout will result in escrowed tokens refunded back. This timeout window is customizable. ### Data Structures @@ -76,13 +76,13 @@ interface AtomicSwapPacketData { } ``` ```typescript -type AtomicSwapPacketAcknowledgement = AtomicSwapPacketAcknowledgementSuccess | AtomicSwapPacketAcknowledgementFailure -interface AtomicSwapPacketAcknowledgementSuccess { +type AtomicSwapPacketAcknowledgement = AtomicSwapPacketSuccess | AtomicSwapPacketError +interface AtomicSwapPacketSuccess { // This is binary 0x01 base64 encoded result: "AQ==" } -interface AtomicSwapPacketAcknowledgementFailure { +interface AtomicSwapPacketError { error: string } ``` @@ -92,11 +92,11 @@ All `AtomicSwapPacketData` will be forwarded to the corresponding message handle ```typescript interface MakeSwapMsg { // the port on which the packet will be sent, specified by the maker when the message is created - sourcePort string + sourcePort: string // the channel on which the packet will be sent, specified by the maker when the message is created sourceChannel: string // the tokens to be exchanged - sellToken : Coin + sellToken: Coin buyToken: Coin // the maker's address makerAddress: string @@ -106,10 +106,10 @@ interface MakeSwapMsg { // only the desiredTaker is allowed to take this order // this is the address on the taker chain desiredTaker: string - creationTimestamp: int64 - expirationTimestamp: int64 - timeoutHeight: int64 - timeoutTimestamp: int64 + creationTimestamp: uint64 + expirationTimestamp: uint64 + timeoutHeight: Height + timeoutTimestamp: uint64 nonce: string // a random 6 digits string } ``` @@ -125,9 +125,9 @@ interface TakeSwapMsg { takerAddress: string // the taker's address on the maker chain takerReceivingAddress: string - creationTimestamp: int64 - timeoutHeight: int64 - timeoutTimestamp: int64 + creationTimestamp: uint64 + timeoutHeight: Height + timeoutTimestamp: uint64 } ``` @@ -137,12 +137,13 @@ interface CancelSwapMsg { sourceChannel: string orderId: string makerAddress: string - timeoutHeight: int64 - timeoutTimestamp: int64 + timeoutHeight: Height + timeoutTimestamp: uint64 } ``` -Note: `Coin` is `sdk.Coin`, +where `Coin` is an interface that consists of an amount and a denomination: + ```ts interface Coin { amount: int64 @@ -161,7 +162,7 @@ enum Status { } interface OrderBook { - id: string; + id: string maker: MakeSwapMsg status: Status // set onReceived(), Make sure that the take order can only be sent to the chain the make order came from @@ -169,8 +170,8 @@ interface OrderBook { // set onReceived(), Make sure that the take order can only be sent to the chain the make order came from channelIdOnTakerChain: string taker: TakeSwapMsg - cancelTimestamp: int64 - completeTimestamp: int64 + cancelTimestamp: uint64 + completeTimestamp: uint64 createOrder(msg: MakeSwapMsg) OrderBook { return { @@ -183,31 +184,29 @@ interface OrderBook { // Order id is a global unique string function generateOrderId(msg MakeSwapMsg) { - cosnt bytes = protobuf.encode(msg) + const bytes = protobuf.encode(msg) return sha265(bytes) } - - ``` ### Life scope and control flow #### Making a swap -1. A maker creates an order on the maker chain with specified parameters (see type `MakeSwap`). The maker's sell tokens are sent to the escrow address owned by the module. The order is saved on the maker chain -2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is also saved on the taker chain. +1. A maker creates an order on the maker chain with specified parameters (see type `MakeSwap`). The maker's sell tokens are sent to the escrow address owned by the module. The order is saved on the maker chain. +2. An `AtomicSwapPacketData` is relayed to the taker chain where in `onRecvPacket` the order is also saved on the taker chain. 3. A packet is subsequently relayed back for acknowledgement. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Taking a swap -1. A taker takes an order on the taker chain by triggering `TakeSwap`. The taker's sell tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expirationTimestamp` -2. An `AtomicSwapPacketData` is relayed to the maker chain where `onRecvPacket` the escrowed tokens are sent to the taker address on the maker chain. +1. A taker takes an order on the taker chain by triggering `TakeSwap`. The taker's sell tokens are sent to the escrow address owned by the module. An order cannot be taken if the current time is later than the `expirationTimestamp`. +2. An `AtomicSwapPacketData` is relayed to the maker chain where in `onRecvPacket` the escrowed tokens are sent to the taker address on the maker chain. 3. A packet is subsequently relayed back for acknowledgement. Upon acknowledgement escrowed tokens on the taker chain are sent to to the maker address on the taker chain. A packet timeout or a failure during `onAcknowledgePacket` will result in a refund of the escrowed tokens. #### Cancelling a swap 1. A maker cancels a previously created order. Expired orders can also be cancelled. -2. An `AtomicSwapPacketData` is relayed to the taker chain where `onRecvPacket` the order is cancelled on the taker chain. If the order is in the process of being taken (a packet with `TakeSwapMsg` is being relayed from the taker chain to the maker chain), the cancellation will be rejected. +2. An `AtomicSwapPacketData` is relayed to the taker chain where in `onRecvPacket` the order is cancelled on the taker chain. If the order is in the process of being taken (a packet with `TakeSwapMsg` is being relayed from the taker chain to the maker chain), the cancellation will be rejected. 3. A packet is relayed back where upon acknowledgement the order on the maker chain is also cancelled. The refund only occurs if the taker chain confirmed the cancellation request. ### Sub-protocols @@ -215,7 +214,7 @@ function generateOrderId(msg MakeSwapMsg) { The sub-protocols described herein should be implemented in a "Fungible Token Swap" module with access to a bank module and to the IBC routing module. ```ts -function makeSwap(request MakeSwapMsg) { +function makeSwap(request: MakeSwapMsg) { const balance = bank.getBalances(request.makerAddress) abortTransactionUnless(balance.amount > request.sellToken.Amount) // gets escrow address by source port and source channel @@ -239,7 +238,7 @@ function makeSwap(request MakeSwapMsg) { ``` ```ts -function takeSwap(request TakeSwapMsg) { +function takeSwap(request: TakeSwapMsg) { const order = store.findOrderById(request.sourceChannel, request.orderId) abortTransactionUnless(order !== null) abortTransactionUnless(order.expirationTimestamp < currentTimestamp()) @@ -249,7 +248,7 @@ function takeSwap(request TakeSwapMsg) { // if `desiredTaker` is set, only the desiredTaker can accept the order. abortTransactionUnless(order.maker.desiredTaker !== null && order.maker.desiredTaker !== request.takerAddress) - // check if this take message send to the correct chain + // check if this take message sent to the correct chain abortTransactionUnless(order.channelIdOnTakerChain === request.sourceChannel) const balance = bank.getBalances(request.takerAddress) @@ -268,23 +267,22 @@ function takeSwap(request TakeSwapMsg) { sendAtomicSwapPacket(packet, order.portIdOnTakerChain, order.channelIdOnTakerChain, request.timeoutHeight, request.timeoutTimestamp) - //update order state + // update order state order.taker = request // mark that the order has been occupied store.save(order) } ``` - ```ts -function cancelSwap(request CancelSwapMsg) { +function cancelSwap(request: CancelSwapMsg) { const order = store.findOrderById(request.sourceChannel, request.orderId) // checks if the order exists - abortTransactionUnless(order != null) + abortTransactionUnless(order !== null) // make sure the sender is the maker of the order. abortTransactionUnless(order.maker.makerAddress == request.makerAddress) abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) - // check if this take message send to the correct chain + // check if this cancel message sent to the correct chain abortTransactionUnless(request.sourceChannel == order.maker.sourceChannel) // constructs the IBC data packet @@ -436,9 +434,9 @@ function sendAtomicSwapPacket( ```typescript function onRecvPacket(packet channeltypes.Packet) { - const swapPaket: AtomicSwapPacketData = packet.data + const swapPaket: AtomicSwapPacketData = packet.data - AtomicSwapPacketAcknowledgement ack = new AtomicSwapPacketAcknowledgementSuccess() + AtomicSwapPacketAcknowledgement ack = AtomicSwapPacketAcknowledgement{true, null} switch swapPaket.type { case TYPE_MSG_MAKE_SWAP: @@ -455,8 +453,8 @@ function onRecvPacket(packet channeltypes.Packet) { order.channelIdOnTakerChain = packet.destinationChannel // saves order to store const err = store.save(order) - if(err != null) { - ack = new AtomicSwapPacketAcknowledgementFailure("Failed to save the order on taker chain") + if (err != null) { + ack = AtomicSwapPacketAcknowledgement{false, "dailed to save the order on taker chain"} } break; case TYPE_MSG_TAKE_SWAP: @@ -474,7 +472,7 @@ function onRecvPacket(packet channeltypes.Packet) { // send maker.sellToken to taker's receiving address const err = bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken) if (err != null) { - ack = new AtomicSwapPacketAcknowledgementFailure("transfer coins failed") + ack = AtomicSwapPacketAcknowledgement{false, "transfer coins failed"} } // update status of order @@ -495,11 +493,11 @@ function onRecvPacket(packet channeltypes.Packet) { order.cancelTimestamp = cancelMsg.creationTimestamp const err = store.save(order) if (err != null) { - ack = new AtomicSwapPacketAcknowledgementFailure("failed to cancel order on taker chain") + ack = AtomicSwapPacketAcknowledgement{false, "failed to cancel order on taker chain"} } break; default: - ack = new AtomicSwapPacketAcknowledgementFailure("unknown data packet") + ack = AtomicSwapPacketAcknowledgement{false, "unknown data packet"} } return ack @@ -510,11 +508,11 @@ function onRecvPacket(packet channeltypes.Packet) { ```typescript function onAcknowledgePacket( - packet channeltypes.Packet + packet: channeltypes.Packet acknowledgement: bytes) { // ack is failed if (!ack.success) { - refundToken(packet) + refundTokens(packet) } else { const swapPaket: AtomicSwapPacketData = protobuf.decode(packet.data) @@ -527,7 +525,7 @@ function onAcknowledgePacket( // update order status on the maker chain. const order = store.findOrderById(makeMsg.sourceChannel, generateOrderId(makeMsg)) order.status = Status.SYNC - //save order to store + // save order to store store.save(order) break; case TYPE_MSG_TAKE_SWAP: @@ -572,7 +570,7 @@ function onAcknowledgePacket( ```typescript function onTimeoutPacket(packet: Packet) { // the packet timed-out, so refund the tokens - refundTokens(packet); + refundTokens(packet) } ``` @@ -583,7 +581,7 @@ function refundTokens(packet: Packet) { const swapPacket: AtomicSwapPacketData = protobuf.decode(packet.data) const escrowAddr = escrowAddress(packet.sourcePort, packet.sourceChannel) - //send tokens from module to message sender + // send tokens from module to message sender switch swapPaket.type { case TYPE_MSG_MAKE_SWAP: const msg = protobuf.decode(swapPacket.data) From 5c4a3bb171e8b14b52ae28bf0f2449414d0457b3 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Tue, 10 Jan 2023 14:40:25 +0100 Subject: [PATCH 55/84] replaced []byte with bytes --- spec/app/ics-100-atomic-swap/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 4cd01e62e..be088df53 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -71,7 +71,7 @@ enum SwapMessageType { // AtomicSwapPacketData is comprised of a swap message type, raw transaction and optional memo field. interface AtomicSwapPacketData { type: SwapMessageType - data: []byte + data: bytes memo: string } ``` @@ -173,11 +173,11 @@ interface OrderBook { cancelTimestamp: uint64 completeTimestamp: uint64 - createOrder(msg: MakeSwapMsg) OrderBook { - return { - id : generateOrderId(msg), - status: Status.INITIAL, - maker: msg + createOrder(msg: MakeSwapMsg): OrderBook { + return OrderBook{ + id : generateOrderId(msg), + status: Status.INITIAL, + maker: msg } } } From 1f935415bc5cc21961d383eb2f4bd8629c795a0c Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Mon, 6 Feb 2023 15:40:11 +0800 Subject: [PATCH 56/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Manuel Bravo --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index be088df53..192500ebb 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -434,7 +434,7 @@ function sendAtomicSwapPacket( ```typescript function onRecvPacket(packet channeltypes.Packet) { - const swapPaket: AtomicSwapPacketData = packet.data + const swapPacket: AtomicSwapPacketData = packet.data AtomicSwapPacketAcknowledgement ack = AtomicSwapPacketAcknowledgement{true, null} From 55831ace93afcb5189f6fe5547cae952be343f7e Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Mon, 6 Feb 2023 17:58:46 +0800 Subject: [PATCH 57/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Manuel Bravo --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 192500ebb..1c232b0a8 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -16,7 +16,7 @@ This standard document specifies packet data structure, state machine handling l ### Motivation -Users may wish to exchange tokens without transfering tokens away from its native chain. ICS-100 enabled chains can facilitate atomic swaps between users and their tokens located on the different chains. This is useful for exchanges between specific users at specific prices, and opens opportunities for new application designs. +Users may wish to exchange tokens without transferring tokens away from their native chain. ICS-100 enabled chains can facilitate atomic swaps between users and their tokens located on the different chains. This is useful for exchanges between specific users at specific prices, and opens opportunities for new application designs. ### Definitions From dd0f3628d0eb617082c31dead75cfc4ba5d19dd1 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Mon, 6 Feb 2023 17:59:04 +0800 Subject: [PATCH 58/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Manuel Bravo --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 1c232b0a8..29bbcc099 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -438,7 +438,7 @@ function onRecvPacket(packet channeltypes.Packet) { AtomicSwapPacketAcknowledgement ack = AtomicSwapPacketAcknowledgement{true, null} - switch swapPaket.type { + switch swapPacket.type { case TYPE_MSG_MAKE_SWAP: const makeMsg = protobuf.decode(swapPaket.data) From df88515ca609b5342636b464f6c2eed70b3d4b65 Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Mon, 6 Feb 2023 18:02:19 +0800 Subject: [PATCH 59/84] replace > with >= --- spec/app/ics-100-atomic-swap/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 29bbcc099..2adc4cebc 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -216,7 +216,7 @@ The sub-protocols described herein should be implemented in a "Fungible Token Sw ```ts function makeSwap(request: MakeSwapMsg) { const balance = bank.getBalances(request.makerAddress) - abortTransactionUnless(balance.amount > request.sellToken.Amount) + abortTransactionUnless(balance.amount >= request.sellToken.Amount) // gets escrow address by source port and source channel const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) // locks the sellToken to the escrow account @@ -252,7 +252,7 @@ function takeSwap(request: TakeSwapMsg) { abortTransactionUnless(order.channelIdOnTakerChain === request.sourceChannel) const balance = bank.getBalances(request.takerAddress) - abortTransactionUnless(balance.amount > request.sellToken.amount) + abortTransactionUnless(balance.amount >= request.sellToken.amount) // gets the escrow address by source port and source channel const escrowAddr = escrowAddress(order.portIdOnTakerChain, order.channelIdOnTakerChain) // locks the sellToken to the escrow account From f349b769095817a9358c5be4b122506490a2effb Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Mon, 6 Feb 2023 18:15:17 +0800 Subject: [PATCH 60/84] use token.amount and denom in sendCoin --- spec/app/ics-100-atomic-swap/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 2adc4cebc..44337cd38 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -220,7 +220,7 @@ function makeSwap(request: MakeSwapMsg) { // gets escrow address by source port and source channel const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) // locks the sellToken to the escrow account - const err = bank.sendCoins(request.makerAddress, escrowAddr, request.sellToken) + const err = bank.sendCoins(request.makerAddress, escrowAddr, request.sellToken.amount, request.sellToken.denom) abortTransactionUnless(err === null) // contructs the IBC data packet const packet = { @@ -256,7 +256,7 @@ function takeSwap(request: TakeSwapMsg) { // gets the escrow address by source port and source channel const escrowAddr = escrowAddress(order.portIdOnTakerChain, order.channelIdOnTakerChain) // locks the sellToken to the escrow account - const err = bank.sendCoins(request.takerAddress, escrowAddr, request.sellToken) + const err = bank.sendCoins(request.takerAddress, escrowAddr, request.sellToken.amount, request.sellToken.denom) abortTransactionUnless(err === null) // constructs the IBC data packet const packet = { @@ -470,7 +470,7 @@ function onRecvPacket(packet channeltypes.Packet) { const escrowAddr = escrowAddress(order.portIdOnTakerChain, order.channelIdOnTakerChain) // send maker.sellToken to taker's receiving address - const err = bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken) + const err = bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken.amount, order.maker.sellToken.denom) if (err != null) { ack = AtomicSwapPacketAcknowledgement{false, "transfer coins failed"} } @@ -535,7 +535,7 @@ function onAcknowledgePacket( const order = store.findOrderById(takeMsg.sourceChannel, takeMsg.orderId) // send tokens to maker - bank.sendCoins(escrowAddr, order.maker.makerReceivingAddress, takeMsg.sellToken) + bank.sendCoins(escrowAddr, order.maker.makerReceivingAddress, takeMsg.sellToken.amount, takeMsg.sellToken.denom) order.status = Status.COMPLETE order.taker = takeMsg @@ -550,7 +550,7 @@ function onAcknowledgePacket( const order = store.findOrderById(cannelMsg.sourceChannel, cancelMsg.orderId) // send tokens back to maker - bank.sendCoins(escrowAddr, order.maker.makerAddress, order.maker.sellToken) + bank.sendCoins(escrowAddr, order.maker.makerAddress, order.maker.sellToken.amount, order.maker.sellToken.denom) // update state on maker chain order.status = Status.CANCEL @@ -585,7 +585,7 @@ function refundTokens(packet: Packet) { switch swapPaket.type { case TYPE_MSG_MAKE_SWAP: const msg = protobuf.decode(swapPacket.data) - bank.sendCoins(escrowAddr, msg.makerAddress, msg.sellToken) + bank.sendCoins(escrowAddr, msg.makerAddress, msg.sellToken.amount, msg.sellToken.denom) const orderId = generateOrderId(msg) const order = store.findOrderById(packet.sourceChannel, orderId) order.status = Status.CANCEL @@ -593,7 +593,7 @@ function refundTokens(packet: Packet) { break; case TYPE_MSG_TAKE_SWAP: const msg = protobuf.decode(swapPacket.data) - bank.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken) + bank.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken.amount, msg.sellToken.denom) const order = store.findOrderById(packet.sourceChannel, msg.orderId) order.taker = null // release the occupation store.save(order) From 28cb0b10618d0e752b8402d0ccbb513ed1f5cb25 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Mon, 6 Feb 2023 19:08:51 +0800 Subject: [PATCH 61/84] change store to privateStore, ICS24 --- spec/app/ics-100-atomic-swap/README.md | 58 ++++++++++++++------------ 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 44337cd38..de2cc7edb 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -151,17 +151,18 @@ interface Coin { } ``` -Both the maker chain and taker chain maintain separate orderbooks. Orders are saved in both maker chain and taker chain. +Both the maker chain and taker chain maintain separate order store. Orders are saved in both maker chain and taker chain. ```typescript +// Status of order enum Status { - INITIAL = 0, - SYNC = 1, - CANCEL = 2, - COMPLETE = 3, + INITIAL = 0, // initialed on maker chain + SYNC = 1, // synced to the taker chain + CANCEL = 2, // canceled + COMPLETE = 3, // completed } -interface OrderBook { +interface Order { id: string maker: MakeSwapMsg status: Status @@ -182,6 +183,11 @@ interface OrderBook { } } +// generate path for store +function orderPath(channel: Identifier, id: Identifier): Path { + return "channel/{channel}/order/{id}" +} + // Order id is a global unique string function generateOrderId(msg MakeSwapMsg) { const bytes = protobuf.encode(msg) @@ -232,14 +238,14 @@ function makeSwap(request: MakeSwapMsg) { // creates and saves order on the maker chain. const order = OrderBook.createOrder(request) - //saves order to store - store.save(order) + // saves order to store + privateStore.set(orderPath(order.sourceChannel, order.orderId), order) } ``` ```ts function takeSwap(request: TakeSwapMsg) { - const order = store.findOrderById(request.sourceChannel, request.orderId) + const order = privateStore.get(orderPath(request.sourceChannel, request.orderId)) abortTransactionUnless(order !== null) abortTransactionUnless(order.expirationTimestamp < currentTimestamp()) abortTransactionUnless(order.maker.buyToken.denom === request.sellToken.denom) @@ -269,13 +275,13 @@ function takeSwap(request: TakeSwapMsg) { // update order state order.taker = request // mark that the order has been occupied - store.save(order) + privateStore.set(orderPath(order.sourceChannel, order.orderId), order) } ``` ```ts function cancelSwap(request: CancelSwapMsg) { - const order = store.findOrderById(request.sourceChannel, request.orderId) + const order = privateStore.get(orderPath(request.sourceChannel, request.orderId)) // checks if the order exists abortTransactionUnless(order !== null) // make sure the sender is the maker of the order. @@ -452,14 +458,14 @@ function onRecvPacket(packet channeltypes.Packet) { order.portIdOnTakerChain = packet.destinationPort order.channelIdOnTakerChain = packet.destinationChannel // saves order to store - const err = store.save(order) + const err = privateStore.set(orderPath(packet.destinationChannel, order.orderId), order) if (err != null) { ack = AtomicSwapPacketAcknowledgement{false, "dailed to save the order on taker chain"} } break; case TYPE_MSG_TAKE_SWAP: const takeMsg = protobuf.decode(swapPaket.data) - const order = store.findOrderById(packet.destinationChannel, takeMsg.orderId) + const order = privateStore.get(orderPath(packet.destinationChannel, takeMsg.orderId)) abortTransactionUnless(order !== null) abortTransactionUnless(order.status === Status.SYNC) abortTransactionUnless(order.expiredTimestamp < currentTimestamp()) @@ -479,11 +485,11 @@ function onRecvPacket(packet channeltypes.Packet) { order.status = Status.COMPLETE order.taker = takeMsg order.completeTimestamp = takeMsg.creationTimestamp - store.save(order) + privateStore.set(orderPath(packet.destinationChannel, takeMsg.orderId), order) break; case TYPE_MSG_CANCEL_SWAP: const cancelMsg = protobuf.decode(swapPaket.data) - const order = store.findOrderById(packet.destinationChannel, cancelMsg.orderId) + const order = privateStore.get(orderPath(packet.destinationChannel, cancelMsg.orderId)) abortTransactionUnless(order !== null) abortTransactionUnless(order.status === Status.SYNC || order.status == Status.INITIAL) abortTransactionUnless(order.taker !== null) // the maker order has not been occupied @@ -491,7 +497,7 @@ function onRecvPacket(packet channeltypes.Packet) { // update status of order order.status = Status.CANCEL order.cancelTimestamp = cancelMsg.creationTimestamp - const err = store.save(order) + const err = privateStore.set(orderPath(packet.destinationChannel, cancelMsg.orderId), order) if (err != null) { ack = AtomicSwapPacketAcknowledgement{false, "failed to cancel order on taker chain"} } @@ -523,16 +529,16 @@ function onAcknowledgePacket( const makeMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = store.findOrderById(makeMsg.sourceChannel, generateOrderId(makeMsg)) + const order = privateStore.get(orderPath(makeMsg.sourceChannel, generateOrderId(makeMsg))) order.status = Status.SYNC // save order to store - store.save(order) + privateStore.set(orderPath(makeMsg.sourceChannel, generateOrderId(makeMsg)), order) break; case TYPE_MSG_TAKE_SWAP: const takeMsg = protobuf.decode(swapPaket.data) // update order status on the taker chain. - const order = store.findOrderById(takeMsg.sourceChannel, takeMsg.orderId) + const order = privateStore.get(orderPath(takeMsg.sourceChannel, takeMsg.orderId)) // send tokens to maker bank.sendCoins(escrowAddr, order.maker.makerReceivingAddress, takeMsg.sellToken.amount, takeMsg.sellToken.denom) @@ -540,14 +546,14 @@ function onAcknowledgePacket( order.status = Status.COMPLETE order.taker = takeMsg order.completeTimestamp = takeMsg.creationTimestamp - store.save(order) + privateStore.set(orderPath(takeMsg.sourceChannel, takeMsg.orderId), order) break; case TYPE_MSG_CANCEL_SWAP: const cancelMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = store.findOrderById(cannelMsg.sourceChannel, cancelMsg.orderId) + const order = privateStore.get(orderPath(cannelMsg.sourceChannel, cancelMsg.orderId)) // send tokens back to maker bank.sendCoins(escrowAddr, order.maker.makerAddress, order.maker.sellToken.amount, order.maker.sellToken.denom) @@ -555,7 +561,7 @@ function onAcknowledgePacket( // update state on maker chain order.status = Status.CANCEL order.cancelTimestamp = cancelMsg.creationTimestamp - store.save(order) + privateStore.set(orderPath(cannelMsg.sourceChannel, cancelMsg.orderId), order) break; default: @@ -587,16 +593,16 @@ function refundTokens(packet: Packet) { const msg = protobuf.decode(swapPacket.data) bank.sendCoins(escrowAddr, msg.makerAddress, msg.sellToken.amount, msg.sellToken.denom) const orderId = generateOrderId(msg) - const order = store.findOrderById(packet.sourceChannel, orderId) + const order = privateStore.get(orderPath(order.sourceChannel, order.orderId)) order.status = Status.CANCEL - store.save(order) + privateStore.set(orderPath(order.sourceChannel, order.orderId), order) break; case TYPE_MSG_TAKE_SWAP: const msg = protobuf.decode(swapPacket.data) bank.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken.amount, msg.sellToken.denom) - const order = store.findOrderById(packet.sourceChannel, msg.orderId) + const order = privateStore.get(orderPath(packet.sourceChannel, msg.orderId)) order.taker = null // release the occupation - store.save(order) + privateStore.set(orderPath(order.sourceChannel, order.orderId), order) break; case TYPE_MSG_CANCEL_SWAP: // do nothing, only send tokens back when cancel msg is acknowledged. From 1698375459791a1474010263b0230343cbd9b1eb Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Mon, 6 Feb 2023 19:51:40 +0800 Subject: [PATCH 62/84] update get balance --- spec/app/ics-100-atomic-swap/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index de2cc7edb..d11de82b4 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -221,7 +221,7 @@ The sub-protocols described herein should be implemented in a "Fungible Token Sw ```ts function makeSwap(request: MakeSwapMsg) { - const balance = bank.getBalances(request.makerAddress) + const balance = bank.getBalances(request.makerAddress, request.sellToken.Amount) abortTransactionUnless(balance.amount >= request.sellToken.Amount) // gets escrow address by source port and source channel const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) @@ -257,7 +257,7 @@ function takeSwap(request: TakeSwapMsg) { // check if this take message sent to the correct chain abortTransactionUnless(order.channelIdOnTakerChain === request.sourceChannel) - const balance = bank.getBalances(request.takerAddress) + const balance = bank.getBalances(request.takerAddress, request.sellToken.Amount) abortTransactionUnless(balance.amount >= request.sellToken.amount) // gets the escrow address by source port and source channel const escrowAddr = escrowAddress(order.portIdOnTakerChain, order.channelIdOnTakerChain) From e15d46b91f4c4a6adb652d336848b9ef8386352c Mon Sep 17 00:00:00 2001 From: EddyG Date: Mon, 6 Feb 2023 08:15:24 -0800 Subject: [PATCH 63/84] added more regarding atomicity --- spec/app/ics-100-atomic-swap/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index d11de82b4..de5b87a53 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -18,9 +18,13 @@ This standard document specifies packet data structure, state machine handling l Users may wish to exchange tokens without transferring tokens away from their native chain. ICS-100 enabled chains can facilitate atomic swaps between users and their tokens located on the different chains. This is useful for exchanges between specific users at specific prices, and opens opportunities for new application designs. +For example, a token exchange would require only one transaction from an user, compared to multiple transactions when using ICS-20. + +Additionally, users can minimize trade slippage compared to using a liquidity pool, given there is a willing counter-party. + ### Definitions -`Atomic Swap`: An exchange of tokens from separate chains without transfering tokens from one blockchain to another. +`Atomic Swap`: An exchange of tokens from separate chains without transfering tokens from one blockchain to another. The exchange either happens or it doesn't -- there is no other alternative. `Order`: An offer to exchange quantity X of token A for quantity Y of token B. Tokens offered are sent to an escrow account (owned by the module). @@ -40,6 +44,7 @@ Users may wish to exchange tokens without transferring tokens away from their na - `Refundable`: tokens are refunded by escrow when a timeout occurs, or when an order is cancelled. - `Order cancellation`: orders without takers can be cancelled. - `Basic orderbook`: a store of orders functioning as an orderbook system. +- `Atomicity`: an exchange of one token for another where it is either a total success or a total failure. ## Technical Specification From 72432520be60669aeaa6ca6899b849678a883f1b Mon Sep 17 00:00:00 2001 From: EddyG Date: Mon, 6 Feb 2023 08:18:31 -0800 Subject: [PATCH 64/84] added more regarding atomicity --- spec/app/ics-100-atomic-swap/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index de5b87a53..2103afd75 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -18,9 +18,7 @@ This standard document specifies packet data structure, state machine handling l Users may wish to exchange tokens without transferring tokens away from their native chain. ICS-100 enabled chains can facilitate atomic swaps between users and their tokens located on the different chains. This is useful for exchanges between specific users at specific prices, and opens opportunities for new application designs. -For example, a token exchange would require only one transaction from an user, compared to multiple transactions when using ICS-20. - -Additionally, users can minimize trade slippage compared to using a liquidity pool, given there is a willing counter-party. +For example, a token exchange would require only one transaction from an user, compared to multiple transactions when using ICS-20. Additionally, users can minimize trade slippage compared to using a liquidity pool, given there is a willing counter-party. ### Definitions From b63cbc45aa9586bb00d8805097904082fa2ef7cc Mon Sep 17 00:00:00 2001 From: ping <18786721@qq.com> Date: Thu, 9 Feb 2023 10:07:14 +0800 Subject: [PATCH 65/84] Update spec/app/ics-100-atomic-swap/README.md Co-authored-by: Aditya --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 2103afd75..7b3293314 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -463,7 +463,7 @@ function onRecvPacket(packet channeltypes.Packet) { // saves order to store const err = privateStore.set(orderPath(packet.destinationChannel, order.orderId), order) if (err != null) { - ack = AtomicSwapPacketAcknowledgement{false, "dailed to save the order on taker chain"} + ack = AtomicSwapPacketAcknowledgement{false, "failed to save the order on taker chain"} } break; case TYPE_MSG_TAKE_SWAP: From d484620c979beadb0da7e0cca5e771378e899e08 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 9 Feb 2023 11:37:55 +0800 Subject: [PATCH 66/84] fixed wrong argument --- spec/app/ics-100-atomic-swap/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index d11de82b4..b163a28b2 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -221,7 +221,7 @@ The sub-protocols described herein should be implemented in a "Fungible Token Sw ```ts function makeSwap(request: MakeSwapMsg) { - const balance = bank.getBalances(request.makerAddress, request.sellToken.Amount) + const balance = bank.getBalances(request.makerAddress, request.sellToken.denom) abortTransactionUnless(balance.amount >= request.sellToken.Amount) // gets escrow address by source port and source channel const escrowAddr = escrowAddress(request.sourcePort, request.sourceChannel) @@ -257,7 +257,7 @@ function takeSwap(request: TakeSwapMsg) { // check if this take message sent to the correct chain abortTransactionUnless(order.channelIdOnTakerChain === request.sourceChannel) - const balance = bank.getBalances(request.takerAddress, request.sellToken.Amount) + const balance = bank.getBalances(request.takerAddress, request.sellToken.denom) abortTransactionUnless(balance.amount >= request.sellToken.amount) // gets the escrow address by source port and source channel const escrowAddr = escrowAddress(order.portIdOnTakerChain, order.channelIdOnTakerChain) From 5e50b078b052a3791d8e43b36c05bb1952d88a50 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 9 Feb 2023 13:03:42 +0800 Subject: [PATCH 67/84] add ibc path to Order. and change algo for order id. --- spec/app/ics-100-atomic-swap/README.md | 103 ++++++++++++------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index f172bbbed..e9f772fc3 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -113,14 +113,11 @@ interface MakeSwapMsg { expirationTimestamp: uint64 timeoutHeight: Height timeoutTimestamp: uint64 - nonce: string // a random 6 digits string } ``` ```typescript interface TakeSwapMsg { - // the channel on which the packet will be sent, specified by the taker when the message is created - sourceChannel: string orderId: string // the tokens to be sold sellToken: Coin @@ -136,8 +133,6 @@ interface TakeSwapMsg { ```typescript interface CancelSwapMsg { - // the channel on which the packet will be sent, specified by the maker when the message is created - sourceChannel: string orderId: string makerAddress: string timeoutHeight: Height @@ -169,32 +164,38 @@ interface Order { id: string maker: MakeSwapMsg status: Status - // set onReceived(), Make sure that the take order can only be sent to the chain the make order came from - portIdOnTakerChain: string - // set onReceived(), Make sure that the take order can only be sent to the chain the make order came from - channelIdOnTakerChain: string + // an IBC path, define channel and port on both Maker Chain and Taker Chain + path: string taker: TakeSwapMsg cancelTimestamp: uint64 completeTimestamp: uint64 - - createOrder(msg: MakeSwapMsg): OrderBook { - return OrderBook{ - id : generateOrderId(msg), +} + +function createOrder(msg: MakeSwapMsg, packet: channelType.Packet): Order { + const path = orderPath(packet) + return Order{ + id : generateOrderId(path, msg), status: Status.INITIAL, - maker: msg + path: path, + maker: msg, } } -} -// generate path for store -function orderPath(channel: Identifier, id: Identifier): Path { - return "channel/{channel}/order/{id}" -} +function orderPath(packet: channelType.Packet): string { + return `channel/${packet.sourceChannel}/port/${packet.sourcePort}/channel/${packet.destChannel}/port/${packet.destPort}/sequence/${packet.sequence}` +} // Order id is a global unique string -function generateOrderId(msg MakeSwapMsg) { +function generateOrderId(path string, msg MakeSwapMsg) : string { const bytes = protobuf.encode(msg) - return sha265(bytes) + return sha265(path.toByte() + bytes) +} + +function extractSourceChannelForTakerMsg(path: string) : string { + return path.split('/')[5] +} +function extractSourcePortForTakerMsg(path: string) : string{ + return path.split('/')[7] } ``` @@ -223,7 +224,7 @@ function generateOrderId(msg MakeSwapMsg) { The sub-protocols described herein should be implemented in a "Fungible Token Swap" module with access to a bank module and to the IBC routing module. ```ts -function makeSwap(request: MakeSwapMsg) { +function makeSwap(request: MakeSwapMsg, packet: channelTypes.Packet) { const balance = bank.getBalances(request.makerAddress, request.sellToken.denom) abortTransactionUnless(balance.amount >= request.sellToken.Amount) // gets escrow address by source port and source channel @@ -240,15 +241,15 @@ function makeSwap(request: MakeSwapMsg) { sendAtomicSwapPacket(packet, request.sourcePort, request.sourceChannel, request.timeoutHeight, request.timeoutTimestamp) // creates and saves order on the maker chain. - const order = OrderBook.createOrder(request) + const order = createOrder(request, packet) // saves order to store - privateStore.set(orderPath(order.sourceChannel, order.orderId), order) + privateStore.set(order.id, order) } ``` ```ts function takeSwap(request: TakeSwapMsg) { - const order = privateStore.get(orderPath(request.sourceChannel, request.orderId)) + const order = privateStore.get(request.orderId) abortTransactionUnless(order !== null) abortTransactionUnless(order.expirationTimestamp < currentTimestamp()) abortTransactionUnless(order.maker.buyToken.denom === request.sellToken.denom) @@ -257,13 +258,14 @@ function takeSwap(request: TakeSwapMsg) { // if `desiredTaker` is set, only the desiredTaker can accept the order. abortTransactionUnless(order.maker.desiredTaker !== null && order.maker.desiredTaker !== request.takerAddress) - // check if this take message sent to the correct chain - abortTransactionUnless(order.channelIdOnTakerChain === request.sourceChannel) - const balance = bank.getBalances(request.takerAddress, request.sellToken.denom) abortTransactionUnless(balance.amount >= request.sellToken.amount) + + const sourceChannel = extractSourceChannelForTakerMsg(order.path) + const sourcePort = extractSourcePortForTakerMsg(order.path) + // gets the escrow address by source port and source channel - const escrowAddr = escrowAddress(order.portIdOnTakerChain, order.channelIdOnTakerChain) + const escrowAddr = escrowAddress(sourcePort, sourceChannel) // locks the sellToken to the escrow account const err = bank.sendCoins(request.takerAddress, escrowAddr, request.sellToken.amount, request.sellToken.denom) abortTransactionUnless(err === null) @@ -274,26 +276,23 @@ function takeSwap(request: TakeSwapMsg) { memo: "" } - sendAtomicSwapPacket(packet, order.portIdOnTakerChain, order.channelIdOnTakerChain, request.timeoutHeight, request.timeoutTimestamp) + sendAtomicSwapPacket(packet, sourcePort, sourceChannel, request.timeoutHeight, request.timeoutTimestamp) // update order state order.taker = request // mark that the order has been occupied - privateStore.set(orderPath(order.sourceChannel, order.orderId), order) + privateStore.set(order.orderId, order) } ``` ```ts function cancelSwap(request: CancelSwapMsg) { - const order = privateStore.get(orderPath(request.sourceChannel, request.orderId)) + const order = privateStore.get(request.orderId) // checks if the order exists abortTransactionUnless(order !== null) // make sure the sender is the maker of the order. abortTransactionUnless(order.maker.makerAddress == request.makerAddress) abortTransactionUnless(order.status == Status.SYNC || order.status == Status.INITIAL) - - // check if this cancel message sent to the correct chain - abortTransactionUnless(request.sourceChannel == order.maker.sourceChannel) - + // constructs the IBC data packet const packet = { type: SwapMessageType.TYPE_MSG_CANCEL_SWAP, @@ -461,14 +460,14 @@ function onRecvPacket(packet channeltypes.Packet) { order.portIdOnTakerChain = packet.destinationPort order.channelIdOnTakerChain = packet.destinationChannel // saves order to store - const err = privateStore.set(orderPath(packet.destinationChannel, order.orderId), order) + const err = privateStore.set(order.orderId, order) if (err != null) { ack = AtomicSwapPacketAcknowledgement{false, "failed to save the order on taker chain"} } break; case TYPE_MSG_TAKE_SWAP: const takeMsg = protobuf.decode(swapPaket.data) - const order = privateStore.get(orderPath(packet.destinationChannel, takeMsg.orderId)) + const order = privateStore.get(takeMsg.orderId) abortTransactionUnless(order !== null) abortTransactionUnless(order.status === Status.SYNC) abortTransactionUnless(order.expiredTimestamp < currentTimestamp()) @@ -488,11 +487,11 @@ function onRecvPacket(packet channeltypes.Packet) { order.status = Status.COMPLETE order.taker = takeMsg order.completeTimestamp = takeMsg.creationTimestamp - privateStore.set(orderPath(packet.destinationChannel, takeMsg.orderId), order) + privateStore.set(takeMsg.orderId, order) break; case TYPE_MSG_CANCEL_SWAP: const cancelMsg = protobuf.decode(swapPaket.data) - const order = privateStore.get(orderPath(packet.destinationChannel, cancelMsg.orderId)) + const order = privateStore.get(cancelMsg.orderId) abortTransactionUnless(order !== null) abortTransactionUnless(order.status === Status.SYNC || order.status == Status.INITIAL) abortTransactionUnless(order.taker !== null) // the maker order has not been occupied @@ -500,7 +499,7 @@ function onRecvPacket(packet channeltypes.Packet) { // update status of order order.status = Status.CANCEL order.cancelTimestamp = cancelMsg.creationTimestamp - const err = privateStore.set(orderPath(packet.destinationChannel, cancelMsg.orderId), order) + const err = privateStore.set(cancelMsg.orderId, order) if (err != null) { ack = AtomicSwapPacketAcknowledgement{false, "failed to cancel order on taker chain"} } @@ -532,16 +531,16 @@ function onAcknowledgePacket( const makeMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = privateStore.get(orderPath(makeMsg.sourceChannel, generateOrderId(makeMsg))) + const order = privateStore.get(generateOrderId(makeMsg, packet)) order.status = Status.SYNC // save order to store - privateStore.set(orderPath(makeMsg.sourceChannel, generateOrderId(makeMsg)), order) + privateStore.set(order.id, order) break; case TYPE_MSG_TAKE_SWAP: const takeMsg = protobuf.decode(swapPaket.data) // update order status on the taker chain. - const order = privateStore.get(orderPath(takeMsg.sourceChannel, takeMsg.orderId)) + const order = privateStore.get(takeMsg.orderId) // send tokens to maker bank.sendCoins(escrowAddr, order.maker.makerReceivingAddress, takeMsg.sellToken.amount, takeMsg.sellToken.denom) @@ -549,14 +548,14 @@ function onAcknowledgePacket( order.status = Status.COMPLETE order.taker = takeMsg order.completeTimestamp = takeMsg.creationTimestamp - privateStore.set(orderPath(takeMsg.sourceChannel, takeMsg.orderId), order) + privateStore.set(order.id, order) break; case TYPE_MSG_CANCEL_SWAP: const cancelMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = privateStore.get(orderPath(cannelMsg.sourceChannel, cancelMsg.orderId)) + const order = privateStore.get( cancelMsg.orderId ) // send tokens back to maker bank.sendCoins(escrowAddr, order.maker.makerAddress, order.maker.sellToken.amount, order.maker.sellToken.denom) @@ -564,7 +563,7 @@ function onAcknowledgePacket( // update state on maker chain order.status = Status.CANCEL order.cancelTimestamp = cancelMsg.creationTimestamp - privateStore.set(orderPath(cannelMsg.sourceChannel, cancelMsg.orderId), order) + privateStore.set(order.id, order) break; default: @@ -595,17 +594,17 @@ function refundTokens(packet: Packet) { case TYPE_MSG_MAKE_SWAP: const msg = protobuf.decode(swapPacket.data) bank.sendCoins(escrowAddr, msg.makerAddress, msg.sellToken.amount, msg.sellToken.denom) - const orderId = generateOrderId(msg) - const order = privateStore.get(orderPath(order.sourceChannel, order.orderId)) + const orderId = generateOrderId(msg, packet) + const order = privateStore.get(order.orderId) order.status = Status.CANCEL - privateStore.set(orderPath(order.sourceChannel, order.orderId), order) + privateStore.set(order.orderId, order) break; case TYPE_MSG_TAKE_SWAP: const msg = protobuf.decode(swapPacket.data) bank.sendCoins(escrowAddr, msg.takerAddress, msg.sellToken.amount, msg.sellToken.denom) - const order = privateStore.get(orderPath(packet.sourceChannel, msg.orderId)) + const order = privateStore.get(msg.orderId) order.taker = null // release the occupation - privateStore.set(orderPath(order.sourceChannel, order.orderId), order) + privateStore.set(order.orderId, order) break; case TYPE_MSG_CANCEL_SWAP: // do nothing, only send tokens back when cancel msg is acknowledged. From b4d421cb8f9955ab069ba223acfa97547b844636 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 9 Feb 2023 13:17:06 +0800 Subject: [PATCH 68/84] write ack error instead of abort execution --- spec/app/ics-100-atomic-swap/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index e9f772fc3..a3373ed05 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -452,7 +452,9 @@ function onRecvPacket(packet channeltypes.Packet) { // check if buyToken is a valid token on the taker chain, could be either native or ibc token const supply = bank.getSupply(makeMsg.buyToken.denom) - abortTransactionUnless(supply > 0) + if (supply <= 0) { + ack = AtomicSwapPacketAcknowledgement{false, "sell token does not exist on the taker chain"} + } // create and save order on the taker chain. const order = OrderBook.createOrder(makeMsg) From e132898823ca2b3f0c9852ab1fabcf91b046e442 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 9 Feb 2023 13:18:24 +0800 Subject: [PATCH 69/84] write ack error instead of abort execution --- spec/app/ics-100-atomic-swap/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index a3373ed05..53e22395d 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -454,6 +454,7 @@ function onRecvPacket(packet channeltypes.Packet) { const supply = bank.getSupply(makeMsg.buyToken.denom) if (supply <= 0) { ack = AtomicSwapPacketAcknowledgement{false, "sell token does not exist on the taker chain"} + break; } // create and save order on the taker chain. @@ -483,6 +484,7 @@ function onRecvPacket(packet channeltypes.Packet) { const err = bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken.amount, order.maker.sellToken.denom) if (err != null) { ack = AtomicSwapPacketAcknowledgement{false, "transfer coins failed"} + break; } // update status of order From b29832fde1080f8dd0f775a7e8be8918697a3af3 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 9 Feb 2023 13:23:39 +0800 Subject: [PATCH 70/84] remove unused code --- spec/app/ics-100-atomic-swap/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 53e22395d..0cfa3e213 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -458,10 +458,8 @@ function onRecvPacket(packet channeltypes.Packet) { } // create and save order on the taker chain. - const order = OrderBook.createOrder(makeMsg) + const order = createOrder(makeMsg, packet) order.status = Status.SYNC - order.portIdOnTakerChain = packet.destinationPort - order.channelIdOnTakerChain = packet.destinationChannel // saves order to store const err = privateStore.set(order.orderId, order) if (err != null) { From 98177c3790bf78ad7ba891978751456bfe04b392 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Thu, 9 Feb 2023 13:35:49 +0800 Subject: [PATCH 71/84] fixed escrow address error --- spec/app/ics-100-atomic-swap/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 0cfa3e213..8d99a9066 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -476,8 +476,8 @@ function onRecvPacket(packet channeltypes.Packet) { abortTransactionUnless(takeMsg.sellToken.amount === order.maker.buyToken.amount) // if `desiredTaker` is set, only the desiredTaker can accept the order. abortTransactionUnless(order.maker.desiredTaker !== null && order.maker.desiredTaker !== takeMsg.takerAddress) - - const escrowAddr = escrowAddress(order.portIdOnTakerChain, order.channelIdOnTakerChain) + + const escrowAddr = escrowAddress(order.maker.sourcePort, order.maker.sourceChannel) // send maker.sellToken to taker's receiving address const err = bank.sendCoins(escrowAddr, takeMsg.takerReceivingAddress, order.maker.sellToken.amount, order.maker.sellToken.denom) if (err != null) { From 5aa3caee983a84e33387058720dd8b8719f44fb9 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Sun, 12 Feb 2023 20:45:56 +0800 Subject: [PATCH 72/84] use packet to generate order id --- spec/app/ics-100-atomic-swap/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 8d99a9066..1035bd78f 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -174,7 +174,7 @@ interface Order { function createOrder(msg: MakeSwapMsg, packet: channelType.Packet): Order { const path = orderPath(packet) return Order{ - id : generateOrderId(path, msg), + id : generateOrderId(packet), status: Status.INITIAL, path: path, maker: msg, @@ -185,10 +185,10 @@ function orderPath(packet: channelType.Packet): string { return `channel/${packet.sourceChannel}/port/${packet.sourcePort}/channel/${packet.destChannel}/port/${packet.destPort}/sequence/${packet.sequence}` } -// Order id is a global unique string -function generateOrderId(path string, msg MakeSwapMsg) : string { - const bytes = protobuf.encode(msg) - return sha265(path.toByte() + bytes) +// Order id is a global unique string, since packet contains sourceChannel, SourcePort, distChannel, distPort, sequence and msg data +function generateOrderId(packet: channelType.Packet) : string { + const bytes = protobuf.encode(packet) + return sha265(bytes) } function extractSourceChannelForTakerMsg(path: string) : string { @@ -533,7 +533,7 @@ function onAcknowledgePacket( const makeMsg = protobuf.decode(swapPaket.data) // update order status on the maker chain. - const order = privateStore.get(generateOrderId(makeMsg, packet)) + const order = privateStore.get(generateOrderId(packet)) order.status = Status.SYNC // save order to store privateStore.set(order.id, order) @@ -596,7 +596,7 @@ function refundTokens(packet: Packet) { case TYPE_MSG_MAKE_SWAP: const msg = protobuf.decode(swapPacket.data) bank.sendCoins(escrowAddr, msg.makerAddress, msg.sellToken.amount, msg.sellToken.denom) - const orderId = generateOrderId(msg, packet) + const orderId = generateOrderId(packet) const order = privateStore.get(order.orderId) order.status = Status.CANCEL privateStore.set(order.orderId, order) From 3a1857c67fdfd74a757e9ffd51cff95f48fb4474 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Wed, 15 Feb 2023 10:33:21 +0800 Subject: [PATCH 73/84] update diagram --- spec/app/ics-100-atomic-swap/ibcswap.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/ibcswap.png b/spec/app/ics-100-atomic-swap/ibcswap.png index 1dcb63be6..2f1e4ab55 100644 --- a/spec/app/ics-100-atomic-swap/ibcswap.png +++ b/spec/app/ics-100-atomic-swap/ibcswap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83afa3c07f945e43e26134495d0b6d17b20fe7f75a076439abdae4e93e434b60 -size 59602 +oid sha256:3cc22cfb0d0349871b7a3140fc095e6acf2c90da460773a59374042ef846c156 +size 71787 From 23ddadae122a2f9299ccb45f7a9a2ad588f2fd7b Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Wed, 15 Feb 2023 11:33:06 +0800 Subject: [PATCH 74/84] add lock / unlock order --- spec/app/ics-100-atomic-swap/ibcswap.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/ibcswap.png b/spec/app/ics-100-atomic-swap/ibcswap.png index 2f1e4ab55..7a490a4a7 100644 --- a/spec/app/ics-100-atomic-swap/ibcswap.png +++ b/spec/app/ics-100-atomic-swap/ibcswap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3cc22cfb0d0349871b7a3140fc095e6acf2c90da460773a59374042ef846c156 -size 71787 +oid sha256:4ffc19b8ff1fdb042c3eeb067facc5ecb5a73ef2f597ac1d722c640aab3d6df6 +size 75539 From 5d218e90afe41910af427a38209754b38d9c67b6 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Wed, 15 Feb 2023 18:03:28 +0800 Subject: [PATCH 75/84] update diagram --- spec/app/ics-100-atomic-swap/ibcswap.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/ibcswap.png b/spec/app/ics-100-atomic-swap/ibcswap.png index 7a490a4a7..ac3e2bd71 100644 --- a/spec/app/ics-100-atomic-swap/ibcswap.png +++ b/spec/app/ics-100-atomic-swap/ibcswap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ffc19b8ff1fdb042c3eeb067facc5ecb5a73ef2f597ac1d722c640aab3d6df6 -size 75539 +oid sha256:9a0deab46a618ed5cd1da7e04a4771b9d4000049077bc94a68d72438dab65bcd +size 78210 From 544419e8fd08ce65e7c59b4c4ce1145cf83aefa5 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Fri, 17 Feb 2023 15:00:19 +0800 Subject: [PATCH 76/84] disable status check for now --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 1035bd78f..284319135 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -470,7 +470,7 @@ function onRecvPacket(packet channeltypes.Packet) { const takeMsg = protobuf.decode(swapPaket.data) const order = privateStore.get(takeMsg.orderId) abortTransactionUnless(order !== null) - abortTransactionUnless(order.status === Status.SYNC) + // abortTransactionUnless(order.status === Status.SYNC) abortTransactionUnless(order.expiredTimestamp < currentTimestamp()) abortTransactionUnless(takeMsg.sellToken.denom === order.maker.buyToken.denom) abortTransactionUnless(takeMsg.sellToken.amount === order.maker.buyToken.amount) From 10f1fe82abff0d429c8257c81889be0ee9145310 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Fri, 17 Feb 2023 15:00:33 +0800 Subject: [PATCH 77/84] update diagram --- spec/app/ics-100-atomic-swap/ibcswap.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/ibcswap.png b/spec/app/ics-100-atomic-swap/ibcswap.png index ac3e2bd71..375c78b3b 100644 --- a/spec/app/ics-100-atomic-swap/ibcswap.png +++ b/spec/app/ics-100-atomic-swap/ibcswap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a0deab46a618ed5cd1da7e04a4771b9d4000049077bc94a68d72438dab65bcd -size 78210 +oid sha256:e2664c61592579867dd5f8b759ef7be8599eca0711f6682035353ed68ed4907a +size 79780 From ff58c9d1d81319fd297afba5bd0296162ff05f22 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Fri, 24 Feb 2023 09:11:28 +0800 Subject: [PATCH 78/84] add token state changing --- spec/app/ics-100-atomic-swap/ibcswap.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/ibcswap.png b/spec/app/ics-100-atomic-swap/ibcswap.png index 375c78b3b..731924c43 100644 --- a/spec/app/ics-100-atomic-swap/ibcswap.png +++ b/spec/app/ics-100-atomic-swap/ibcswap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2664c61592579867dd5f8b759ef7be8599eca0711f6682035353ed68ed4907a -size 79780 +oid sha256:11d4644b65bc0572c6eff52184cddd06fb7a1e6323ec4c9bac8bcc462881862d +size 82384 From c3a5d59d922e6cfa675e4536e020bd18730b6161 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Fri, 24 Feb 2023 09:22:29 +0800 Subject: [PATCH 79/84] use Cancel Request instead of Cancel Order --- spec/app/ics-100-atomic-swap/ibcswap.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/app/ics-100-atomic-swap/ibcswap.png b/spec/app/ics-100-atomic-swap/ibcswap.png index 731924c43..de432b492 100644 --- a/spec/app/ics-100-atomic-swap/ibcswap.png +++ b/spec/app/ics-100-atomic-swap/ibcswap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11d4644b65bc0572c6eff52184cddd06fb7a1e6323ec4c9bac8bcc462881862d -size 82384 +oid sha256:9566375943632bf32b3c298b02b5d58829cf35c8740e9d03ba223c54bdcf4668 +size 82287 From ac20c37d88aae29c585dec04c972215a0d1c7c97 Mon Sep 17 00:00:00 2001 From: liangping <18786721@qq.com> Date: Fri, 24 Feb 2023 11:06:20 +0800 Subject: [PATCH 80/84] add status checker back on receive taker Msg --- spec/app/ics-100-atomic-swap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 284319135..562046b63 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -470,7 +470,7 @@ function onRecvPacket(packet channeltypes.Packet) { const takeMsg = protobuf.decode(swapPaket.data) const order = privateStore.get(takeMsg.orderId) abortTransactionUnless(order !== null) - // abortTransactionUnless(order.status === Status.SYNC) + abortTransactionUnless(order.status === Status.SYNC) abortTransactionUnless(order.expiredTimestamp < currentTimestamp()) abortTransactionUnless(takeMsg.sellToken.denom === order.maker.buyToken.denom) abortTransactionUnless(takeMsg.sellToken.amount === order.maker.buyToken.amount) From 4034e473088711ef53fd15346cceeb922c36962b Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 27 Feb 2023 22:03:16 +0100 Subject: [PATCH 81/84] Update CODEOWNERS --- .github/CODEOWNERS | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 299f24a08..81aaa9a22 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,16 @@ # Default owners for repository # 2/n quorum required for merge -* @mpoke @adityasripal @cwgoes @colin-axner \ No newline at end of file +* @mpoke @adityasripal @cwgoes @colin-axner @angbrav + +# CODEOWNERS for the CODEOWNER file + +/.github/CODEOWNERS @mpoke @adityasripal @cwgoes @colin-axner @angbrav + +# CODEOWNERS for the specs + +/spec/app @adityasripal @cwgoes @colin-axner @angbrav + +/spec/app/ics-028-cross-chain-validation @mpoke @adityasripal @cwgoes @angbrav + +/spec/app/ics-100-atomic-swap ping@side.one edward@s16.ventures @adityasripal @cwgoes @angbrav From 53dbbaf9d1aaff702a9336d277ec4e11c6dfd090 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 27 Feb 2023 22:04:48 +0100 Subject: [PATCH 82/84] Update CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 81aaa9a22..6090a5e60 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,4 +13,4 @@ /spec/app/ics-028-cross-chain-validation @mpoke @adityasripal @cwgoes @angbrav -/spec/app/ics-100-atomic-swap ping@side.one edward@s16.ventures @adityasripal @cwgoes @angbrav +/spec/app/ics-100-atomic-swap @liangping @egunawan85 @adityasripal @cwgoes @angbrav From ab74221073e32eb2a25121cdaccb7ac407539537 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 27 Feb 2023 22:18:07 +0100 Subject: [PATCH 83/84] add banner message at the top of spec --- spec/app/ics-100-atomic-swap/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/app/ics-100-atomic-swap/README.md b/spec/app/ics-100-atomic-swap/README.md index 562046b63..5ad052518 100644 --- a/spec/app/ics-100-atomic-swap/README.md +++ b/spec/app/ics-100-atomic-swap/README.md @@ -10,6 +10,8 @@ created: 2022-07-27 modified: 2022-10-07 --- +

This specification has been reviewed by the Spec Comittee, but it is not responsible for maintaining it.

+ ## Synopsis This standard document specifies packet data structure, state machine handling logic, and encoding details for the atomic swap of fungible tokens over an IBC channel between two modules on separate chains. From 0689d432bd863f7e30ccc7880bc505233296e8c5 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Mon, 27 Feb 2023 22:20:47 +0100 Subject: [PATCH 84/84] Update CODEOWNERS --- .github/CODEOWNERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6090a5e60..e3a37ad7e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,5 +12,3 @@ /spec/app @adityasripal @cwgoes @colin-axner @angbrav /spec/app/ics-028-cross-chain-validation @mpoke @adityasripal @cwgoes @angbrav - -/spec/app/ics-100-atomic-swap @liangping @egunawan85 @adityasripal @cwgoes @angbrav