├── .cargo_vcs_info.json ├── .gitignore ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── README.md ├── artifacts ├── checksums.txt ├── ics101.wasm ├── lp_staking.wasm ├── sample_project.wasm ├── staking_contract.wasm ├── ve_side.wasm └── volume.wasm ├── codegen ├── cosmoswasm-codegen │ ├── Ics100.client.ts │ ├── Ics100.provider.ts │ ├── Ics100.react-query.ts │ ├── Ics100.types.ts │ ├── Ics101.client.ts │ ├── Ics101.provider.ts │ ├── Ics101.react-query.ts │ ├── Ics101.types.ts │ ├── contractContextBase.ts │ ├── contractContextProviders.ts │ ├── contracts-context.tsx │ └── index.ts ├── index.ts ├── package.json ├── tsconfig.json ├── yarn-error.log └── yarn.lock ├── contracts ├── ics101 │ ├── Cargo.toml │ ├── INTEGRATION.md │ ├── examples │ │ └── schema.rs │ ├── response.proto │ ├── schema │ │ ├── execute_msg.json │ │ ├── instantiate_msg.json │ │ └── query_msg.json │ └── src │ │ ├── approx_pow.rs │ │ ├── contract.rs │ │ ├── error.rs │ │ ├── ibc.rs │ │ ├── interchainswap_handler.rs │ │ ├── lib.rs │ │ ├── market.rs │ │ ├── math.rs │ │ ├── msg.rs │ │ ├── response.rs │ │ ├── state.rs │ │ ├── types.rs │ │ └── utils.rs ├── lp-staking │ ├── .DS_Store │ ├── Cargo.toml │ ├── README.md │ ├── examples │ │ └── schema.rs │ ├── schema │ │ ├── execute_msg.json │ │ ├── instantiate_msg.json │ │ └── query_msg.json │ └── src │ │ ├── contract.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── msg.rs │ │ ├── query.rs │ │ └── state.rs ├── router │ ├── Cargo.toml │ ├── LICENSE │ ├── NOTICE │ ├── README.md │ ├── examples │ │ └── schema.rs │ ├── schema │ │ ├── constants.json │ │ ├── count_response.json │ │ ├── execute_msg.json │ │ ├── instantiate_msg.json │ │ └── query_msg.json │ └── src │ │ ├── contract.rs │ │ ├── error.rs │ │ ├── interaction_gmm.rs │ │ ├── lib.rs │ │ ├── msg.rs │ │ ├── querier.rs │ │ ├── query.rs │ │ └── state.rs ├── staking_contract │ ├── Cargo.toml │ ├── README.md │ ├── examples │ │ └── schema.rs │ └── src │ │ ├── admin.rs │ │ ├── claim.rs │ │ ├── constants.rs │ │ ├── contract.rs │ │ ├── deposit.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── msg.rs │ │ ├── query.rs │ │ ├── receive.rs │ │ ├── staking.rs │ │ ├── state.rs │ │ ├── tokens.rs │ │ ├── types │ │ ├── config.rs │ │ ├── killswitch.rs │ │ ├── mod.rs │ │ ├── validator_set.rs │ │ ├── window_manager.rs │ │ └── withdraw_window.rs │ │ ├── update.rs │ │ ├── utils.rs │ │ ├── window.rs │ │ └── withdraw.rs ├── volume │ ├── .DS_Store │ ├── Cargo.toml │ ├── examples │ │ └── schema.rs │ ├── schema │ │ ├── execute_msg.json │ │ ├── instantiate_msg.json │ │ └── query_msg.json │ └── src │ │ ├── contract.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── msg.rs │ │ └── state.rs └── voting-escrow │ ├── Cargo.toml │ ├── README.md │ ├── examples │ └── schema.rs │ ├── schema │ ├── raw │ │ ├── execute.json │ │ ├── instantiate.json │ │ ├── migrate.json │ │ ├── query.json │ │ ├── response_to_balance.json │ │ ├── response_to_config.json │ │ ├── response_to_lock_info.json │ │ ├── response_to_simulate_lock.json │ │ ├── response_to_token_info.json │ │ ├── response_to_total_voting_power.json │ │ ├── response_to_total_voting_power_at.json │ │ ├── response_to_total_voting_power_at_period.json │ │ ├── response_to_user_deposit_at_height.json │ │ ├── response_to_user_voting_power.json │ │ ├── response_to_user_voting_power_at.json │ │ └── response_to_user_voting_power_at_period.json │ └── ve-side.json │ └── src │ ├── contract.rs │ ├── error.rs │ ├── lib.rs │ ├── msg.rs │ ├── state.rs │ └── utils.rs ├── docs ├── ics100.md └── ics101.md ├── ics100_swap.wasm ├── schema ├── execute_msg.json ├── instantiate_msg.json └── query_msg.json ├── scripts ├── cancel-swap.sh ├── check-balance.sh ├── deploy-contract-malaga.sh ├── deploy-contract-wasmd.sh ├── details-query.sh ├── init-contract-malaga.sh ├── init-contract-wasmd.sh ├── list-swap.sh ├── make-swap-sidechain.sh ├── make-swap-target.sh ├── make-swap.sh ├── rly-create-channel-with-sidechain.sh ├── rly-create-channel.sh ├── run-makerchain.sh ├── run-takerchain.sh ├── sidechain.json ├── source.json ├── take-swap-sidechain.sh ├── take-swap-source.sh ├── take-swap-target.sh ├── take-swap.sh └── target.json └── testnet-configurations ├── ics100.md └── ics101.md /.cargo_vcs_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "sha1": "cb3c89201527aba339d77b81e3044f773e715275" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | /.DS_Store 4 | contracts/.DS_Store 5 | contracts/ics100/.DS_Store 6 | contracts/ics101/.DS_Store 7 | node_modules 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "interchain" 4 | ], 5 | "rust-analyzer.linkedProjects": [ 6 | "./contracts/lp-staking/Cargo.toml" 7 | ] 8 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["contracts/*"] 3 | 4 | [profile.release.package.ics100] 5 | codegen-units = 1 6 | incremental = false 7 | 8 | [profile.release.package.ics101] 9 | codegen-units = 1 10 | incremental = false 11 | 12 | [profile.release] 13 | rpath = false 14 | lto = true 15 | overflow-checks = true 16 | opt-level = 3 17 | debug = false 18 | debug-assertions = false 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ibcswap-wasm 2 | 3 | The current state of the project is still in development and not yet ready for production use. 4 | 5 | ibcswap-wasm is a Cosmwasm implementation of the ICS 100 and ICS 101 specifications within the Inter-Blockchain Communication (IBC) protocol of the Cosmos ecosystem. ICS 100 specifies the atomic swap functionality, while ICS 101 specifies the interchain swap functionality. As a community-driven project, ibcswap-wasm aims to provide a secure and efficient solution for cross-chain token exchanges, following the ICS 100 and ICS 101 specifications. Currently in active development, ibcswap-wasm holds the potential to enable seamless interoperability between Cosmos-based blockchains and empower decentralized finance (DeFi) applications. 6 | 7 | # Documentation 8 | 9 | - [ICS100](./docs/ics100.md) 10 | - [ICS101](./docs/ics101.md) 11 | 12 | 13 | 14 | 15 | # run ts-codegen 16 | 17 | [TS-Codegen](https://github.com/CosmWasm/ts-codegen#readme) 18 | 19 | ```bash 20 | cd codegen 21 | yarn 22 | yarn ts-codegen 23 | ``` -------------------------------------------------------------------------------- /artifacts/checksums.txt: -------------------------------------------------------------------------------- 1 | 18d634a3b55c84adec22f7355e357bc22ab90cb6d9acf227255821e6d437c41c ics101.wasm 2 | fa74d20f0a8efe508566b5bae530ca8f236e40e73174cc3da36728a94b573e6b lp_staking.wasm 3 | b969eef23b68ed91510fc90d7f38697758c0762ca1c770158ce645dbeaac9141 sample_project.wasm 4 | 77731d93e5c8ab2e5418ef2e18643efffb53a4424e9c3d1a262902909e67fbc2 staking_contract.wasm 5 | 65aabbd91963a9dd1c0341102a34f2befd03e4b9481d66de4b9c8d80276ce524 ve_side.wasm 6 | 1e8a98c424c77dc6bc1742f5f1160b27cd0d1232bc63ece398a1c79a833c83ca volume.wasm 7 | -------------------------------------------------------------------------------- /artifacts/ics101.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideprotocol/mesh-liquidity-wasm/6a7f5f1f7cd2344311f6bfe17ac0be41bfe782a2/artifacts/ics101.wasm -------------------------------------------------------------------------------- /artifacts/lp_staking.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideprotocol/mesh-liquidity-wasm/6a7f5f1f7cd2344311f6bfe17ac0be41bfe782a2/artifacts/lp_staking.wasm -------------------------------------------------------------------------------- /artifacts/sample_project.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideprotocol/mesh-liquidity-wasm/6a7f5f1f7cd2344311f6bfe17ac0be41bfe782a2/artifacts/sample_project.wasm -------------------------------------------------------------------------------- /artifacts/staking_contract.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideprotocol/mesh-liquidity-wasm/6a7f5f1f7cd2344311f6bfe17ac0be41bfe782a2/artifacts/staking_contract.wasm -------------------------------------------------------------------------------- /artifacts/ve_side.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideprotocol/mesh-liquidity-wasm/6a7f5f1f7cd2344311f6bfe17ac0be41bfe782a2/artifacts/ve_side.wasm -------------------------------------------------------------------------------- /artifacts/volume.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideprotocol/mesh-liquidity-wasm/6a7f5f1f7cd2344311f6bfe17ac0be41bfe782a2/artifacts/volume.wasm -------------------------------------------------------------------------------- /codegen/cosmoswasm-codegen/Ics100.provider.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. 3 | * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, 4 | * and run the @cosmwasm/ts-codegen generate command to regenerate this file. 5 | */ 6 | 7 | import { ContractBase, IContractConstructor, IEmptyClient } from "./contractContextBase"; 8 | import { Ics100Client, Ics100QueryClient } from "./Ics100.client"; 9 | export class Ics100 extends ContractBase { 10 | constructor({ 11 | address, 12 | cosmWasmClient, 13 | signingCosmWasmClient 14 | }: IContractConstructor) { 15 | super(address, cosmWasmClient, signingCosmWasmClient, Ics100Client, Ics100QueryClient, undefined); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /codegen/cosmoswasm-codegen/Ics100.types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. 3 | * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, 4 | * and run the @cosmwasm/ts-codegen generate command to regenerate this file. 5 | */ 6 | 7 | export type Timestamp = Uint64; 8 | export type Uint64 = string; 9 | export type Uint128 = string; 10 | export type Status = "INITIAL" | "SYNC" | "CANCEL" | "COMPLETE"; 11 | export interface DetailsResponse { 12 | cancel_timestamp?: Timestamp | null; 13 | complete_timestamp?: Timestamp | null; 14 | id: string; 15 | maker: MakeSwapMsg; 16 | path: string; 17 | status: Status; 18 | taker?: TakeSwapMsg | null; 19 | [k: string]: unknown; 20 | } 21 | export interface MakeSwapMsg { 22 | buy_token: Coin; 23 | desired_taker: string; 24 | expiration_timestamp: number; 25 | maker_address: string; 26 | maker_receiving_address: string; 27 | sell_token: Coin; 28 | source_channel: string; 29 | source_port: string; 30 | take_bids: boolean; 31 | timeout_height: Height; 32 | timeout_timestamp: number; 33 | [k: string]: unknown; 34 | } 35 | export interface Coin { 36 | amount: Uint128; 37 | denom: string; 38 | [k: string]: unknown; 39 | } 40 | export interface Height { 41 | revision_height: number; 42 | revision_number: number; 43 | [k: string]: unknown; 44 | } 45 | export interface TakeSwapMsg { 46 | order_id: string; 47 | sell_token: Coin; 48 | taker_address: string; 49 | taker_receiving_address: string; 50 | timeout_height: Height; 51 | timeout_timestamp: number; 52 | [k: string]: unknown; 53 | } 54 | export type ExecuteMsg = { 55 | MakeSwap: MakeSwapMsg; 56 | } | { 57 | TakeSwap: TakeSwapMsg; 58 | } | { 59 | CancelSwap: CancelSwapMsg; 60 | } | { 61 | MakeBid: MakeBidMsg; 62 | } | { 63 | TakeBid: TakeBidMsg; 64 | } | { 65 | CancelBid: CancelBidMsg; 66 | }; 67 | export interface CancelSwapMsg { 68 | maker_address: string; 69 | order_id: string; 70 | timeout_height: HeightOutput; 71 | timeout_timestamp: string; 72 | [k: string]: unknown; 73 | } 74 | export interface HeightOutput { 75 | revision_height: string; 76 | revision_number: string; 77 | [k: string]: unknown; 78 | } 79 | export interface MakeBidMsg { 80 | order_id: string; 81 | sell_token: Coin; 82 | taker_address: string; 83 | taker_receiving_address: string; 84 | [k: string]: unknown; 85 | } 86 | export interface TakeBidMsg { 87 | bidder: string; 88 | order_id: string; 89 | [k: string]: unknown; 90 | } 91 | export interface CancelBidMsg { 92 | bidder: string; 93 | order_id: string; 94 | [k: string]: unknown; 95 | } 96 | export interface InstantiateMsg { 97 | [k: string]: unknown; 98 | } 99 | export type Side = "NATIVE" | "REMOTE"; 100 | export interface ListResponse { 101 | swaps: AtomicSwapOrder[]; 102 | [k: string]: unknown; 103 | } 104 | export interface AtomicSwapOrder { 105 | cancel_timestamp?: Timestamp | null; 106 | complete_timestamp?: Timestamp | null; 107 | create_timestamp: number; 108 | id: string; 109 | maker: MakeSwapMsg; 110 | path: string; 111 | side: Side; 112 | status: Status; 113 | taker?: TakeSwapMsg | null; 114 | [k: string]: unknown; 115 | } 116 | export type QueryMsg = { 117 | list: { 118 | limit?: number | null; 119 | start_after?: string | null; 120 | [k: string]: unknown; 121 | }; 122 | } | { 123 | list_by_desired_taker: { 124 | desired_taker: string; 125 | limit?: number | null; 126 | start_after?: string | null; 127 | [k: string]: unknown; 128 | }; 129 | } | { 130 | list_by_maker: { 131 | limit?: number | null; 132 | maker: string; 133 | start_after?: string | null; 134 | [k: string]: unknown; 135 | }; 136 | } | { 137 | list_by_taker: { 138 | limit?: number | null; 139 | start_after?: string | null; 140 | taker: string; 141 | [k: string]: unknown; 142 | }; 143 | } | { 144 | details: { 145 | id: string; 146 | [k: string]: unknown; 147 | }; 148 | } | { 149 | bid_detailsby_order: { 150 | limit?: number | null; 151 | order_id: string; 152 | start_after?: string | null; 153 | [k: string]: unknown; 154 | }; 155 | } | { 156 | bid_detailsby_bidder: { 157 | bidder: string; 158 | order_id: string; 159 | [k: string]: unknown; 160 | }; 161 | }; -------------------------------------------------------------------------------- /codegen/cosmoswasm-codegen/Ics101.provider.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. 3 | * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, 4 | * and run the @cosmwasm/ts-codegen generate command to regenerate this file. 5 | */ 6 | 7 | import { ContractBase, IContractConstructor, IEmptyClient } from "./contractContextBase"; 8 | import { Ics101Client, Ics101QueryClient } from "./Ics101.client"; 9 | export class Ics101 extends ContractBase { 10 | constructor({ 11 | address, 12 | cosmWasmClient, 13 | signingCosmWasmClient 14 | }: IContractConstructor) { 15 | super(address, cosmWasmClient, signingCosmWasmClient, Ics101Client, Ics101QueryClient, undefined); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /codegen/cosmoswasm-codegen/Ics101.types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. 3 | * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, 4 | * and run the @cosmwasm/ts-codegen generate command to regenerate this file. 5 | */ 6 | 7 | export type ExecuteMsg = { 8 | MakePool: MsgMakePoolRequest; 9 | } | { 10 | TakePool: MsgTakePoolRequest; 11 | } | { 12 | CancelPool: MsgCancelPoolRequest; 13 | } | { 14 | SingleAssetDeposit: MsgSingleAssetDepositRequest; 15 | } | { 16 | MakeMultiAssetDeposit: MsgMakeMultiAssetDepositRequest; 17 | } | { 18 | CancelMultiAssetDeposit: MsgCancelMultiAssetDepositRequest; 19 | } | { 20 | TakeMultiAssetDeposit: MsgTakeMultiAssetDepositRequest; 21 | } | { 22 | MultiAssetWithdraw: MsgMultiAssetWithdrawRequest; 23 | } | { 24 | Swap: MsgSwapRequest; 25 | } | { 26 | RemovePool: MsgRemovePool; 27 | }; 28 | export type Uint128 = string; 29 | export type PoolSide = "SOURCE" | "DESTINATION"; 30 | export type SwapMsgType = "LEFT" | "RIGHT"; 31 | export interface MsgMakePoolRequest { 32 | counterpartyChannel: string; 33 | counterpartyCreator: string; 34 | creator: string; 35 | destinationChainId: string; 36 | liquidity: PoolAsset[]; 37 | sourceChainId: string; 38 | sourceChannel: string; 39 | sourcePort: string; 40 | swapFee: number; 41 | timeoutHeight: number; 42 | timeoutTimestamp: number; 43 | [k: string]: unknown; 44 | } 45 | export interface PoolAsset { 46 | balance: Coin; 47 | decimal: number; 48 | side: PoolSide; 49 | weight: number; 50 | [k: string]: unknown; 51 | } 52 | export interface Coin { 53 | amount: Uint128; 54 | denom: string; 55 | [k: string]: unknown; 56 | } 57 | export interface MsgTakePoolRequest { 58 | counterCreator: string; 59 | creator: string; 60 | poolId: string; 61 | timeoutHeight: number; 62 | timeoutTimestamp: number; 63 | [k: string]: unknown; 64 | } 65 | export interface MsgCancelPoolRequest { 66 | poolId: string; 67 | timeoutHeight: number; 68 | timeoutTimestamp: number; 69 | [k: string]: unknown; 70 | } 71 | export interface MsgSingleAssetDepositRequest { 72 | poolId: string; 73 | sender: string; 74 | timeoutHeight: number; 75 | timeoutTimestamp: number; 76 | token: Coin; 77 | [k: string]: unknown; 78 | } 79 | export interface MsgMakeMultiAssetDepositRequest { 80 | chainId: string; 81 | deposits: DepositAsset[]; 82 | poolId: string; 83 | timeoutHeight: number; 84 | timeoutTimestamp: number; 85 | [k: string]: unknown; 86 | } 87 | export interface DepositAsset { 88 | balance: Coin; 89 | sender: string; 90 | [k: string]: unknown; 91 | } 92 | export interface MsgCancelMultiAssetDepositRequest { 93 | orderId: string; 94 | poolId: string; 95 | sender: string; 96 | timeoutHeight: number; 97 | timeoutTimestamp: number; 98 | [k: string]: unknown; 99 | } 100 | export interface MsgTakeMultiAssetDepositRequest { 101 | orderId: string; 102 | poolId: string; 103 | sender: string; 104 | timeoutHeight: number; 105 | timeoutTimestamp: number; 106 | [k: string]: unknown; 107 | } 108 | export interface MsgMultiAssetWithdrawRequest { 109 | counterpartyReceiver: string; 110 | poolId: string; 111 | poolToken: Coin; 112 | receiver: string; 113 | timeoutHeight: number; 114 | timeoutTimestamp: number; 115 | [k: string]: unknown; 116 | } 117 | export interface MsgSwapRequest { 118 | poolId: string; 119 | recipient: string; 120 | sender: string; 121 | slippage: number; 122 | swapType: SwapMsgType; 123 | timeoutHeight: number; 124 | timeoutTimestamp: number; 125 | tokenIn: Coin; 126 | tokenOut: Coin; 127 | [k: string]: unknown; 128 | } 129 | export interface MsgRemovePool { 130 | poolId: string; 131 | [k: string]: unknown; 132 | } 133 | export interface InstantiateMsg { 134 | token_code_id: number; 135 | [k: string]: unknown; 136 | } 137 | export type QueryMsg = { 138 | OrderList: { 139 | limit?: number | null; 140 | start_after?: string | null; 141 | [k: string]: unknown; 142 | }; 143 | } | { 144 | Order: { 145 | order_id: string; 146 | pool_id: string; 147 | [k: string]: unknown; 148 | }; 149 | } | { 150 | Config: { 151 | [k: string]: unknown; 152 | }; 153 | } | { 154 | PoolTokenList: { 155 | limit?: number | null; 156 | start_after?: string | null; 157 | [k: string]: unknown; 158 | }; 159 | } | { 160 | PoolAddressByToken: { 161 | pool_id: string; 162 | [k: string]: unknown; 163 | }; 164 | } | { 165 | InterchainPool: { 166 | pool_id: string; 167 | [k: string]: unknown; 168 | }; 169 | } | { 170 | InterchainPoolList: { 171 | limit?: number | null; 172 | start_after?: string | null; 173 | [k: string]: unknown; 174 | }; 175 | } | { 176 | LeftSwap: { 177 | pool_id: string; 178 | token_in: Coin; 179 | token_out: Coin; 180 | [k: string]: unknown; 181 | }; 182 | } | { 183 | RightSwap: { 184 | pool_id: string; 185 | token_in: Coin; 186 | token_out: Coin; 187 | [k: string]: unknown; 188 | }; 189 | } | { 190 | QueryActiveOrders: { 191 | destination_taker: string; 192 | pool_id: string; 193 | source_maker: string; 194 | [k: string]: unknown; 195 | }; 196 | } | { 197 | Rate: { 198 | amount: Uint128; 199 | pool_id: string; 200 | [k: string]: unknown; 201 | }; 202 | }; -------------------------------------------------------------------------------- /codegen/cosmoswasm-codegen/contractContextBase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file and any referenced files were automatically generated by @cosmwasm/ts-codegen@0.35.3 3 | * DO NOT MODIFY BY HAND. Instead, download the latest proto files for your chain 4 | * and run the transpile command or yarn proto command to regenerate this bundle. 5 | */ 6 | 7 | 8 | import { 9 | CosmWasmClient, 10 | SigningCosmWasmClient, 11 | } from '@cosmjs/cosmwasm-stargate'; 12 | 13 | export interface IContractConstructor { 14 | address: string | undefined; 15 | cosmWasmClient: CosmWasmClient | undefined; 16 | signingCosmWasmClient: SigningCosmWasmClient | undefined; 17 | } 18 | 19 | export const NO_SINGING_ERROR_MESSAGE = 'signingCosmWasmClient not connected'; 20 | 21 | export const NO_COSMWASW_CLIENT_ERROR_MESSAGE = 'cosmWasmClient not connected'; 22 | 23 | export const NO_ADDRESS_ERROR_MESSAGE = "address doesn't exist"; 24 | 25 | export const NO_SIGNING_CLIENT_ERROR_MESSAGE = 26 | 'Signing client is not generated. Please check ts-codegen config'; 27 | 28 | export const NO_QUERY_CLIENT_ERROR_MESSAGE = 29 | 'Query client is not generated. Please check ts-codegen config'; 30 | 31 | export const NO_MESSAGE_COMPOSER_ERROR_MESSAGE = 32 | 'Message composer client is not generated. Please check ts-codegen config'; 33 | 34 | /** 35 | * a placeholder for non-generated classes 36 | */ 37 | export interface IEmptyClient {} 38 | 39 | export interface ISigningClientProvider { 40 | getSigningClient(contractAddr: string): T; 41 | } 42 | 43 | export interface IQueryClientProvider { 44 | getQueryClient(contractAddr: string): T; 45 | } 46 | 47 | export interface IMessageComposerProvider { 48 | getMessageComposer(contractAddr: string): T; 49 | } 50 | 51 | export class ContractBase< 52 | TSign = IEmptyClient, 53 | TQuery = IEmptyClient, 54 | TMsgComposer = IEmptyClient 55 | > { 56 | constructor( 57 | protected address: string | undefined, 58 | protected cosmWasmClient: CosmWasmClient | undefined, 59 | protected signingCosmWasmClient: SigningCosmWasmClient | undefined, 60 | private TSign?: new ( 61 | client: SigningCosmWasmClient, 62 | sender: string, 63 | contractAddress: string 64 | ) => TSign, 65 | private TQuery?: new ( 66 | client: CosmWasmClient, 67 | contractAddress: string 68 | ) => TQuery, 69 | private TMsgComposer?: new ( 70 | sender: string, 71 | contractAddress: string 72 | ) => TMsgComposer 73 | ) {} 74 | 75 | public getSigningClient(contractAddr: string): TSign { 76 | if (!this.signingCosmWasmClient) throw new Error(NO_SINGING_ERROR_MESSAGE); 77 | if (!this.address) throw new Error(NO_ADDRESS_ERROR_MESSAGE); 78 | if (!this.TSign) throw new Error(NO_SIGNING_CLIENT_ERROR_MESSAGE); 79 | return new this.TSign( 80 | this.signingCosmWasmClient, 81 | this.address, 82 | contractAddr 83 | ); 84 | } 85 | 86 | public getQueryClient(contractAddr: string): TQuery { 87 | if (!this.cosmWasmClient) throw new Error(NO_COSMWASW_CLIENT_ERROR_MESSAGE); 88 | if (!this.TQuery) throw new Error(NO_QUERY_CLIENT_ERROR_MESSAGE); 89 | return new this.TQuery(this.cosmWasmClient, contractAddr); 90 | } 91 | 92 | public getMessageComposer(contractAddr: string): TMsgComposer { 93 | if (!this.address) throw new Error(NO_ADDRESS_ERROR_MESSAGE); 94 | if (!this.TMsgComposer) throw new Error(NO_MESSAGE_COMPOSER_ERROR_MESSAGE); 95 | return new this.TMsgComposer(this.address, contractAddr); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /codegen/cosmoswasm-codegen/contractContextProviders.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. 3 | * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, 4 | * and run the @cosmwasm/ts-codegen generate command to regenerate this file. 5 | */ 6 | 7 | import { CosmWasmClient, SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; 8 | import { IQueryClientProvider, ISigningClientProvider, IMessageComposerProvider } from "./contractContextBase"; 9 | import { Ics100QueryClient } from "./Ics100.client"; 10 | import { Ics100Client } from "./Ics100.client"; 11 | import { Ics100 } from "./Ics100.provider"; 12 | import { Ics101QueryClient } from "./Ics101.client"; 13 | import { Ics101Client } from "./Ics101.client"; 14 | import { Ics101 } from "./Ics101.provider"; 15 | export interface IContractsContext { 16 | ics100: IQueryClientProvider & ISigningClientProvider; 17 | ics101: IQueryClientProvider & ISigningClientProvider; 18 | } 19 | export const getProviders = (address?: string, cosmWasmClient?: CosmWasmClient, signingCosmWasmClient?: SigningCosmWasmClient) => ({ 20 | ics100: new Ics100({ 21 | address, 22 | cosmWasmClient, 23 | signingCosmWasmClient 24 | }), 25 | ics101: new Ics101({ 26 | address, 27 | cosmWasmClient, 28 | signingCosmWasmClient 29 | }) 30 | }); -------------------------------------------------------------------------------- /codegen/cosmoswasm-codegen/contracts-context.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * This file and any referenced files were automatically generated by @cosmwasm/ts-codegen@0.35.3 3 | * DO NOT MODIFY BY HAND. Instead, download the latest proto files for your chain 4 | * and run the transpile command or yarn proto command to regenerate this bundle. 5 | */ 6 | 7 | 8 | import React, { useEffect, useMemo, useRef, useState, useContext } from 'react'; 9 | import { 10 | CosmWasmClient, 11 | SigningCosmWasmClient, 12 | } from '@cosmjs/cosmwasm-stargate'; 13 | 14 | import { IContractsContext, getProviders } from './contractContextProviders'; 15 | 16 | export interface ContractsConfig { 17 | address: string | undefined; 18 | getCosmWasmClient: () => Promise; 19 | getSigningCosmWasmClient: () => Promise; 20 | } 21 | 22 | const ContractsContext = React.createContext(null); 23 | 24 | export const ContractsProvider = ({ 25 | children, 26 | contractsConfig, 27 | }: { 28 | children: React.ReactNode; 29 | contractsConfig: ContractsConfig; 30 | }) => { 31 | const [cosmWasmClient, setCosmWasmClient] = useState(); 32 | const [signingCosmWasmClient, setSigningCosmWasmClient] = 33 | useState(); 34 | 35 | const { address, getCosmWasmClient, getSigningCosmWasmClient } = 36 | contractsConfig; 37 | 38 | const prevAddressRef = useRef(address); 39 | 40 | const contracts: IContractsContext = useMemo(() => { 41 | return getProviders(address, cosmWasmClient, signingCosmWasmClient); 42 | }, [address, cosmWasmClient, signingCosmWasmClient]); 43 | 44 | useEffect(() => { 45 | const connectSigningCwClient = async () => { 46 | if (address && prevAddressRef.current !== address) { 47 | const signingCosmWasmClient = await getSigningCosmWasmClient(); 48 | setSigningCosmWasmClient(signingCosmWasmClient); 49 | } else if (!address) { 50 | setSigningCosmWasmClient(undefined); 51 | } 52 | prevAddressRef.current = address; 53 | }; 54 | connectSigningCwClient(); 55 | }, [address, getSigningCosmWasmClient]); 56 | 57 | useEffect(() => { 58 | const connectCosmWasmClient = async () => { 59 | const cosmWasmClient = await getCosmWasmClient(); 60 | setCosmWasmClient(cosmWasmClient); 61 | }; 62 | connectCosmWasmClient(); 63 | }, [getCosmWasmClient]); 64 | 65 | return ( 66 | 67 | {children} 68 | 69 | ); 70 | }; 71 | 72 | export const useContracts = () => { 73 | const contracts: IContractsContext = useContext(ContractsContext); 74 | if (contracts === null) { 75 | throw new Error('useContracts must be used within a ContractsProvider'); 76 | } 77 | return contracts; 78 | }; 79 | -------------------------------------------------------------------------------- /codegen/cosmoswasm-codegen/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. 3 | * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, 4 | * and run the @cosmwasm/ts-codegen generate command to regenerate this file. 5 | */ 6 | 7 | import * as _0 from "./Ics100.types"; 8 | import * as _1 from "./Ics100.client"; 9 | import * as _2 from "./Ics100.react-query"; 10 | import * as _3 from "./Ics100.provider"; 11 | import * as _4 from "./Ics101.types"; 12 | import * as _5 from "./Ics101.client"; 13 | import * as _6 from "./Ics101.react-query"; 14 | import * as _7 from "./Ics101.provider"; 15 | import * as _8 from "./contractContextProviders"; 16 | import * as _9 from "./contractContextBase"; 17 | import * as _10 from "./contracts-context"; 18 | export namespace SideContracts { 19 | export const Ics100 = { ..._0, 20 | ..._1, 21 | ..._2, 22 | ..._3 23 | }; 24 | export const Ics101 = { ..._4, 25 | ..._5, 26 | ..._6, 27 | ..._7 28 | }; 29 | export const contractContextProviders = { ..._8 30 | }; 31 | export const contractContextBase = { ..._9 32 | }; 33 | export const contractsContext = { ..._10 34 | }; 35 | } -------------------------------------------------------------------------------- /codegen/index.ts: -------------------------------------------------------------------------------- 1 | import codegen from '@cosmwasm/ts-codegen'; 2 | 3 | codegen({ 4 | contracts: [ 5 | { 6 | name: 'ics100', 7 | dir: '../contracts/ics100/schema' 8 | }, 9 | { 10 | name: 'ics101', 11 | dir: '../contracts/ics101/schema' 12 | } 13 | ], 14 | outPath: './cosmoswasm-codegen', 15 | 16 | // options are completely optional ;) 17 | options: { 18 | bundle: { 19 | bundleFile: 'index.ts', 20 | scope: 'SideContracts' 21 | }, 22 | types: { 23 | enabled: true 24 | }, 25 | client: { 26 | enabled: true 27 | }, 28 | reactQuery: { 29 | enabled: true, 30 | optionalClient: true, 31 | version: 'v4', 32 | mutations: true, 33 | queryKeys: true, 34 | queryFactory: true, 35 | }, 36 | recoil: { 37 | enabled: false 38 | }, 39 | messageComposer: { 40 | enabled: false 41 | }, 42 | messageBuilder: { 43 | enabled: false 44 | }, 45 | useContractsHooks: { 46 | enabled: true 47 | } 48 | } 49 | }).then(() => { 50 | console.log('✨ all done!'); 51 | }); -------------------------------------------------------------------------------- /codegen/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ibcswap-wasm-codegen", 3 | "private": true, 4 | "version": "0.0.0", 5 | "dependencies": { 6 | "@cosmwasm/ts-codegen": "^0.35.3", 7 | "ts-node": "^10.9.1", 8 | "typescript": "^5.1.6" 9 | }, 10 | "scripts": { 11 | "ts-codegen": "yarn ts-node index.ts" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /contracts/ics101/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ics101" 3 | version = "0.1.3" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | cosmwasm-std = {version = "1.2.1", features = ["stargate"]} 8 | cw-storage-plus = {version = "1.0.1"} 9 | cw-utils = {version = "1.0.1"} 10 | cw2 = {version = "1.0.1"} 11 | cw20 = {version = "1.0.1"} 12 | hex = {version = "0.3.1"} 13 | schemars = {version = "0.8.1"} 14 | serde = {version = "1.0.103", default-features = false, features = ["derive"]} 15 | sha2 = {version = "0.8.0"} 16 | thiserror = {version = "1.0.23"} 17 | protobuf = { version = "2", features = ["with-bytes"] } 18 | base64 = "0.21.7" 19 | [dev-dependencies] 20 | cosmwasm-schema = {version = "1.2.1"} 21 | 22 | [features] 23 | backtraces = ["cosmwasm-std/backtraces"] 24 | library = [] 25 | 26 | [lib] 27 | crate-type = ["cdylib", "rlib"] 28 | -------------------------------------------------------------------------------- /contracts/ics101/INTEGRATION.md: -------------------------------------------------------------------------------- 1 | # This document contains interfaces to interact with ics101 contracts 2 | 3 | ## Execution Messages 4 | 1. Make Pool 5 | - Function: `MakePool` 6 | ``` 7 | { 8 | sourcePort: `wasm.${getWasmChainIDContractAddress(chain.chainID)}`, 9 | sourceChannel: getChannelIdsByChainID(chain.chainID, remoteChain.chainID), 10 | counterpartyChannel: getChannelIdsByChainID( 11 | remoteChain.chainID, 12 | chain.chainID 13 | ), 14 | creator: nativeAddress, 15 | counterpartyCreator: remoteAddress, 16 | liquidity: [ 17 | { 18 | side: 'SOURCE', 19 | balance: { 20 | denom: poolCreateStore.native.coin.denom, 21 | amount: getUnitAmount( 22 | poolCreateStore.native.amount, 23 | poolCreateStore.native.coin.denom 24 | ), 25 | }, 26 | weight: 50, 27 | decimal: parseInt(chain?.assets?.[0].exponent), 28 | }, 29 | { 30 | side: 'DESTINATION', 31 | balance: { 32 | denom: poolCreateStore.remote.coin.denom, 33 | amount: getUnitAmount( 34 | poolCreateStore.remote.amount, 35 | poolCreateStore.remote.coin.denom 36 | ), 37 | }, 38 | weight: 50, 39 | decimal: parseInt(remoteChain?.assets?.[0].exponent), 40 | }, 41 | ], 42 | swapFee: poolCreateStore.feeRatio * 100, 43 | timeoutHeight: 100, 44 | timeoutTimestamp: 100, 45 | sourceChainId: chain.chainID, 46 | destinationChainId: remoteChain.chainID, 47 | } 48 | ``` 49 | 50 | 2. Take Pool 51 | - Function: `TakePool` 52 | ``` 53 | { 54 | creator: remoteAddress, 55 | counterCreator: nativeAddress, 56 | poolId: , 57 | timeoutHeight: 100, 58 | timeoutTimestamp: 100, 59 | } 60 | ``` 61 | 62 | 3. Cancel Pool 63 | - Function: `CancelPool` 64 | ``` 65 | { 66 | poolId: , 67 | timeoutHeight: 100, 68 | timeoutTimestamp: 100, 69 | } 70 | ``` 71 | 72 | 4. Make Multi Asset Order 73 | - Function: `MakeMultiAssetDeposit` 74 | ``` 75 | { 76 | chainId: chain.chainID, 77 | poolId: poolItem.id, 78 | deposits: [sourceAsset, targetAsset], // Here sourceAsset and targetAsset type is DepositAsset 79 | timeoutHeight: 100, 80 | timeoutTimestamp: 100, 81 | } 82 | ``` 83 | 84 | DepositAsset: Example 85 | ``` 86 | const targetAsset: DepositAsset = { 87 | sender: remoteAddress, // string 88 | balance: remoteDepositCoin, // Here remoteDepositCoin type `Coin` 89 | }; 90 | ``` 91 | 92 | 5. Take Multi Asset Order 93 | - Function: `TakeMultiAssetDeposit` 94 | ``` 95 | { 96 | poolId: poolDepositStore.poolDepositLastOrder?.poolId, 97 | orderId: poolDepositStore.poolDepositLastOrder?.id, 98 | sender: poolDepositStore.poolDepositLastOrder?.destinationTaker, 99 | timeoutHeight: 100, 100 | timeoutTimestamp: 100, 101 | } 102 | ``` 103 | 104 | 6. Cancel Multi Asset Order 105 | - Function: `CancelMultiAssetDeposit` 106 | ``` 107 | { 108 | poolId: poolDepositStore.poolDepositLastOrder?.poolId, 109 | orderId: poolDepositStore.poolDepositLastOrder?.id, 110 | sender: poolDepositStore.poolDepositLastOrder?.destinationTaker, 111 | timeoutHeight: 100, 112 | timeoutTimestamp: 100, 113 | } 114 | ``` 115 | 116 | 7. Single Asset Order 117 | - Function: `SingleAssetDeposit` 118 | ``` 119 | { 120 | sender: walletAddress, 121 | poolId: , 122 | token: deposit, 123 | timeoutHeight: 100, 124 | timeoutTimestamp: 100, 125 | } 126 | ``` 127 | 128 | 8. Withdraw Asset 129 | Withdraw asset has two steps: 130 | 1. Increase allowance: Message will sent to lp token address of that pool. 131 | - Function: `IncreaseAllowance` 132 | ``` 133 | { 134 | spender: getWasmChainIDContractAddress(chain.chainID), 135 | amount: amount, 136 | } 137 | ``` 138 | 139 | 2. Call to ics101 contract address 140 | - Function: `MultiAssetWithdraw` 141 | ``` 142 | { 143 | poolId: poolItem.id, 144 | receiver: nativeAddress, 145 | counterpartyReceiver: counterPartyAddress, 146 | poolToken: { 147 | denom: poolItem.id, 148 | amount: amount, 149 | }, 150 | timeoutHeight: 100, 151 | timeoutTimestamp: 100, 152 | } 153 | ``` 154 | 155 | 9. Swap 156 | - Function: `MultiAssetWithdraw` 157 | ``` 158 | { 159 | sender: nativeAddress, 160 | swapType: 'LEFT', 161 | poolId: swapStore.selectedPool?.id, 162 | tokenIn, // Type is `Coin` 163 | tokenOut,// Type is `Coin` 164 | slippage: 1000, 165 | recipient: remoteAddress, 166 | timeoutHeight: 100, 167 | timeoutTimestamp: 100, 168 | } 169 | ``` 170 | 171 | For more information about how to call contract. Please refer to [Code](https://github.com/sideprotocol/sidex-ui-priviate/tree/dev/src/api/wasm/services) 172 | 173 | ## Query Interfaces -------------------------------------------------------------------------------- /contracts/ics101/examples/schema.rs: -------------------------------------------------------------------------------- 1 | use std::env::current_dir; 2 | use std::fs::create_dir_all; 3 | 4 | use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; 5 | 6 | // use ics101::msg::DetailsResponse; 7 | use ics101::msg::ExecuteMsg; 8 | use ics101::msg::InstantiateMsg; 9 | // use ics101::msg::ListResponse; 10 | use ics101::msg::QueryMsg; 11 | 12 | fn main() { 13 | let mut out_dir = current_dir().unwrap(); 14 | out_dir.push("schema"); 15 | create_dir_all(&out_dir).unwrap(); 16 | remove_schemas(&out_dir).unwrap(); 17 | 18 | export_schema(&schema_for!(InstantiateMsg), &out_dir); 19 | export_schema(&schema_for!(ExecuteMsg), &out_dir); 20 | export_schema(&schema_for!(QueryMsg), &out_dir); 21 | // export_schema(&schema_for!(ListResponse), &out_dir); 22 | // export_schema(&schema_for!(DetailsResponse), &out_dir); 23 | } 24 | -------------------------------------------------------------------------------- /contracts/ics101/response.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | // MsgInstantiateContractResponse defines the Msg/InstantiateContract response type. 4 | message MsgInstantiateContractResponse { 5 | // ContractAddress is the bech32 address of the new contract instance. 6 | string contract_address = 1; 7 | // Data contains base64-encoded bytes to returned from the contract 8 | bytes data = 2; 9 | } -------------------------------------------------------------------------------- /contracts/ics101/schema/instantiate_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "type": "object", 5 | "required": [ 6 | "token_code_id" 7 | ], 8 | "properties": { 9 | "token_code_id": { 10 | "type": "integer", 11 | "format": "uint64", 12 | "minimum": 0.0 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts/ics101/src/approx_pow.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use cosmwasm_std::{Decimal, StdError, StdResult, Uint128}; 4 | 5 | const DECIMAL_FRACTIONAL: Uint128 = Uint128::new(1_000_000_000_000_000_000u128); 6 | 7 | /// Returns mod subtraction and boolean indicating if the result is negative 8 | fn sub_sign(a: Decimal, b: Decimal) -> (Decimal, bool) { 9 | if a >= b { 10 | (a - b, false) 11 | } else { 12 | (b - a, true) 13 | } 14 | } 15 | 16 | // Pow computes base^(exp) 17 | // However since the exponent is not an integer, we must do an approximation algorithm. 18 | // This implementation is inspired from Osmosis - https://github.com/osmosis-labs/osmosis/blob/1e80a2a220911cbd776f68e8fa5655870a5f5d98/osmomath/math.go#L53 19 | // https://github.com/dexter-zone/dexter_core/tree/main/contracts/pools/weighted_pool 20 | pub fn calculate_pow( 21 | base: Decimal, 22 | exp: Decimal, 23 | precision: Option, 24 | ) -> StdResult { 25 | let precision = precision.unwrap_or(Decimal::from_str("0.00000001").unwrap()); 26 | if base.is_zero() && !exp.is_zero() { 27 | return Ok(base); 28 | } 29 | 30 | // we can adjust the algorithm in this setting. 31 | if base > Decimal::from_ratio(2u128, 1u128) { 32 | // 2 / 1 = 2 33 | return Err(StdError::generic_err( 34 | "calculate_pow : base must be less than 2", 35 | )); 36 | } 37 | 38 | // We will use an approximation algorithm to compute the power. 39 | // Since computing an integer power is easy, we split up the exponent into 40 | // an integer component and a fractional component. 41 | let integer = exp.atomics() / DECIMAL_FRACTIONAL; 42 | let fractional = 43 | Decimal::from_atomics(exp.atomics() % DECIMAL_FRACTIONAL, Decimal::DECIMAL_PLACES) 44 | .map_err(|e| StdError::generic_err(e.to_string()))?; 45 | let integer_pow = base.checked_pow(integer.u128() as u32)?; 46 | 47 | if fractional.is_zero() { 48 | return Ok(integer_pow); 49 | } 50 | 51 | // Contract: 0 < base <= 2 52 | // 0 <= exp < 1. 53 | let fractional_pow = pow_approx(base, fractional, precision)?; 54 | let result = integer_pow.checked_mul(fractional_pow)?; 55 | Ok(result) 56 | } 57 | 58 | // Contract: 0 < base <= 2 59 | // 0 <= exp < 1. 60 | pub fn pow_approx(base: Decimal, exp: Decimal, precision: Decimal) -> StdResult { 61 | // Common case optimization 62 | // Optimize for it being equal to one-half 63 | if exp.eq(&Decimal::from_ratio(1u128, 2u128)) { 64 | return Ok(base.sqrt()); 65 | } 66 | 67 | // We compute this via taking the maclaurin series of (1 + x)^a 68 | // where x = base - 1. 69 | // The maclaurin series of (1 + x)^a = sum_{k=0}^{infty} binom(a, k) x^k 70 | // Binom(a, k) takes the natural continuation on the first parameter, namely that 71 | // Binom(a, k) = N/D, where D = k!, and N = a(a-1)(a-2)...(a-k+1) 72 | // Next we show that the absolute value of each term is less than the last term. 73 | // Note that the change in term n's value vs term n + 1 is a multiplicative factor of 74 | // v_n = x(a - n) / (n+1) 75 | // So if |v_n| < 1, we know that each term has a lesser impact on the result than the last. 76 | // For our bounds on |x| < 1, |a| < 1, 77 | // it suffices to see for what n is |v_n| < 1, 78 | // in the worst parameterization of x = 1, a = -1. 79 | // v_n = |(-1 + epsilon - n) / (n+1)| 80 | // So |v_n| is always less than 1, as n ranges over the integers. 81 | // 82 | // Note that term_n of the expansion is 1 * prod_{i=0}^{n-1} v_i 83 | // The error if we stop the expansion at term_n is: 84 | // error_n = sum_{k=n+1}^{infty} term_k 85 | // At this point we further restrict a >= 0, so 0 <= a < 1. 86 | // Now we take the _INCORRECT_ assumption that if term_n < p, then 87 | // error_n < p. 88 | // This assumption is obviously wrong. 89 | // However our usages of this function don't use the full domain. 90 | // With a > 0, |x| << 1, and p sufficiently low, perhaps this actually is true. 91 | 92 | // :-_-: If theres a bug, balancer and osmosis are also wrong here :-_-: 93 | 94 | let base = base; 95 | let (x, xneg) = sub_sign(base, Decimal::one()); 96 | let mut term = Decimal::one(); 97 | let mut sum = Decimal::one(); 98 | let mut negative = false; 99 | 100 | let a = exp; 101 | let mut big_k = Decimal::zero(); 102 | 103 | let mut i = 1u128; 104 | while term >= precision { 105 | // At each iteration, we need two values, i and i-1. 106 | // To avoid expensive big.Int allocation, we reuse bigK variable. 107 | let (c, cneg) = sub_sign(a, big_k); 108 | 109 | // On this line, bigK == i. 110 | big_k = Decimal::from_ratio(Uint128::from(i), 1u128); // i = i / 1 111 | 112 | term = term 113 | .checked_mul(c)? 114 | .checked_mul(x)? 115 | .checked_div(big_k) 116 | .map_err(|e| StdError::generic_err(e.to_string()))?; 117 | 118 | if term.is_zero() { 119 | break; 120 | } 121 | if xneg { 122 | negative = !negative 123 | } 124 | 125 | if cneg { 126 | negative = !negative 127 | } 128 | 129 | if negative { 130 | sum = sum.checked_sub(term)?; 131 | } else { 132 | sum = sum.checked_add(term)?; 133 | } 134 | i += 1; 135 | } 136 | Ok(sum) 137 | } 138 | 139 | #[cfg(test)] 140 | mod tests { 141 | use super::*; 142 | #[test] 143 | fn check_approx_pow() { 144 | // 1.45^1.5 145 | let mut res = calculate_pow( 146 | Decimal::from_str("1.45").unwrap(), 147 | Decimal::from_str("1.5").unwrap(), 148 | Some(Decimal::from_str("0.00000001").unwrap()), 149 | ); 150 | assert_eq!( 151 | &res.as_ref().unwrap().clone().to_string()[0..10], 152 | "1.74603121" 153 | ); 154 | 155 | // 1.05^3.5 156 | res = calculate_pow( 157 | Decimal::from_str("1.05").unwrap(), 158 | Decimal::from_str("3.5").unwrap(), 159 | Some(Decimal::from_str("0.00000001").unwrap()), 160 | ); 161 | assert_eq!( 162 | &res.as_ref().unwrap().clone().to_string()[0..11], 163 | "1.186212638" 164 | ); 165 | 166 | // 0.91^1.85 167 | res = calculate_pow( 168 | Decimal::from_str("0.91").unwrap(), 169 | Decimal::from_str("1.85").unwrap(), 170 | Some(Decimal::from_str("0.00000001").unwrap()), 171 | ); 172 | assert_eq!( 173 | &res.as_ref().unwrap().clone().to_string()[0..11], 174 | "0.839898055" 175 | ); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /contracts/ics101/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum Never {} 6 | 7 | #[derive(Error, Debug, PartialEq)] 8 | pub enum ContractError { 9 | #[error("{0}")] 10 | Std(#[from] StdError), 11 | 12 | #[error("Send some coins to create an atomic swap")] 13 | EmptyBalance {}, 14 | 15 | #[error("Atomic swap not yet expired")] 16 | NotExpired, 17 | 18 | #[error("Expired atomic swap")] 19 | Expired, 20 | 21 | #[error("Atomic swap already exists")] 22 | AlreadyExists, 23 | 24 | #[error("Order already taken")] 25 | OrderTaken, 26 | 27 | #[error("Order is not for this chain")] 28 | InvalidChain, 29 | 30 | #[error("Invalid sell token")] 31 | InvalidSellToken, 32 | 33 | #[error("Order has already been taken")] 34 | AlreadyTakenOrder, 35 | 36 | #[error("Invalid taker address")] 37 | InvalidTakerAddress, 38 | 39 | #[error("Invalid maker address")] 40 | InvalidMakerAddress, 41 | 42 | #[error("Invalid sender address")] 43 | InvalidSender, 44 | 45 | #[error("Invalid status")] 46 | InvalidStatus, 47 | 48 | #[error("Got a submessage reply with unknown id: {id}")] 49 | UnknownReplyId { id: u64 }, 50 | 51 | #[error("Pool is not ready for swap!")] 52 | NotReadyForSwap, 53 | 54 | #[error("Only supports channel with ibc version ics100-1, got {version}")] 55 | InvalidIbcVersion { version: String }, 56 | 57 | #[error("Only supports unordered channel")] 58 | OnlyOrderedChannel {}, 59 | 60 | #[error("Only accepts tokens that originate on this chain, not native tokens of remote chain")] 61 | NoForeignTokens {}, 62 | 63 | #[error("Parsed port from denom ({port}) doesn't match packet")] 64 | FromOtherPort { port: String }, 65 | 66 | #[error("Parsed channel from denom ({channel}) doesn't match packet")] 67 | FromOtherChannel { channel: String }, 68 | 69 | #[error("Invalid denom pair")] 70 | InvalidDenomPair, 71 | 72 | #[error("Invalid decimal pair")] 73 | InvalidDecimalPair, 74 | 75 | #[error("Invalid weight pair")] 76 | InvalidWeightPair, 77 | 78 | #[error("Invalid amount")] 79 | InvalidAmount, 80 | 81 | #[error("Invalid slippage")] 82 | InvalidSlippage, 83 | 84 | #[error("Invalid pair ratio")] 85 | InvalidPairRatio, 86 | 87 | #[error("Failed to withdraw")] 88 | FailedWithdraw { err: String }, 89 | 90 | #[error("Failed to swap")] 91 | FailedSwap { err: String }, 92 | 93 | #[error("Failed to on swap received")] 94 | FailedOnSwapReceived { err: String }, 95 | 96 | #[error("Invalid swap type")] 97 | InvalidSwapType, 98 | 99 | #[error("Invalid assets input length")] 100 | InvalidAssetInput, 101 | 102 | #[error("Error previous order is not completed")] 103 | ErrPreviousOrderNotCompleted, 104 | 105 | #[error("Error order is already completed")] 106 | ErrOrderAlreadyCompleted, 107 | 108 | #[error("Order not found")] 109 | ErrOrderNotFound, 110 | 111 | #[error("Error failed multi asset deposit")] 112 | ErrFailedMultiAssetDeposit, 113 | } 114 | -------------------------------------------------------------------------------- /contracts/ics101/src/ibc.rs: -------------------------------------------------------------------------------- 1 | // use cw20::{Balance, Cw20ExecuteMsg}; 2 | 3 | use crate::{ 4 | error::{ContractError, Never}, 5 | interchainswap_handler::{ 6 | ack_fail, do_ibc_packet_receive, on_packet_failure, on_packet_success, 7 | }, 8 | utils::{enforce_order_and_version, try_get_ack_error}, 9 | }; 10 | use cosmwasm_std::{ 11 | attr, entry_point, DepsMut, Env, IbcBasicResponse, IbcChannel, IbcChannelCloseMsg, 12 | IbcChannelConnectMsg, IbcChannelOpenMsg, IbcPacketAckMsg, IbcPacketReceiveMsg, 13 | IbcPacketTimeoutMsg, IbcReceiveResponse, 14 | }; 15 | 16 | use crate::state::{ChannelInfo, CHANNEL_INFO}; 17 | 18 | pub const RECEIVE_ID: u64 = 1337; 19 | pub const ACK_FAILURE_ID: u64 = 0xfa17; 20 | 21 | #[cfg_attr(not(feature = "library"), entry_point)] 22 | /// enforces ordering and versioning constraints 23 | pub fn ibc_channel_open( 24 | _deps: DepsMut, 25 | _env: Env, 26 | msg: IbcChannelOpenMsg, 27 | ) -> Result<(), ContractError> { 28 | enforce_order_and_version(msg.channel(), msg.counterparty_version())?; 29 | Ok(()) 30 | } 31 | 32 | #[cfg_attr(not(feature = "library"), entry_point)] 33 | /// record the channel in CHANNEL_INFO 34 | pub fn ibc_channel_connect( 35 | deps: DepsMut, 36 | _env: Env, 37 | msg: IbcChannelConnectMsg, 38 | ) -> Result { 39 | // we need to check the counter party version in try and ack (sometimes here) 40 | enforce_order_and_version(msg.channel(), msg.counterparty_version())?; 41 | 42 | let channel: IbcChannel = msg.into(); 43 | let info = ChannelInfo { 44 | id: channel.endpoint.channel_id, 45 | counterparty_endpoint: channel.counterparty_endpoint, 46 | connection_id: channel.connection_id, 47 | }; 48 | CHANNEL_INFO.save(deps.storage, &info.id, &info)?; 49 | 50 | Ok(IbcBasicResponse::default()) 51 | } 52 | 53 | #[cfg_attr(not(feature = "library"), entry_point)] 54 | pub fn ibc_channel_close( 55 | _deps: DepsMut, 56 | _env: Env, 57 | _channel: IbcChannelCloseMsg, 58 | ) -> Result { 59 | // TODO: what to do here? 60 | // we will have locked funds that need to be returned somehow 61 | unimplemented!(); 62 | } 63 | 64 | #[cfg_attr(not(feature = "library"), entry_point)] 65 | /// Check to see if we have any balance here 66 | /// We should not return an error if possible, but rather an acknowledgement of failure 67 | pub fn ibc_packet_receive( 68 | deps: DepsMut, 69 | _env: Env, 70 | msg: IbcPacketReceiveMsg, 71 | ) -> Result { 72 | let packet = msg.packet; 73 | 74 | do_ibc_packet_receive(deps, _env, &packet).or_else(|err| { 75 | Ok(IbcReceiveResponse::new() 76 | .set_ack(ack_fail(err.to_string())) 77 | .add_attributes(vec![ 78 | attr("action", "receive"), 79 | attr("success", "false"), 80 | attr("error", err.to_string()), 81 | ])) 82 | }) 83 | } 84 | 85 | #[cfg_attr(not(feature = "library"), entry_point)] 86 | // check if success or failure and update balance, or return funds 87 | pub fn ibc_packet_ack( 88 | deps: DepsMut, 89 | _env: Env, 90 | msg: IbcPacketAckMsg, 91 | ) -> Result { 92 | if let Some(error) = try_get_ack_error(&msg.acknowledgement) { 93 | on_packet_failure(deps, msg.original_packet, error) 94 | } else { 95 | on_packet_success(deps, msg.original_packet) 96 | } 97 | } 98 | 99 | #[cfg_attr(not(feature = "library"), entry_point)] 100 | /// return fund to original sender (same as failure in ibc_packet_ack) 101 | pub fn ibc_packet_timeout( 102 | deps: DepsMut, 103 | _env: Env, 104 | msg: IbcPacketTimeoutMsg, 105 | ) -> Result { 106 | let packet = msg.packet; 107 | on_packet_failure(deps, packet, "timeout".to_string()) 108 | } 109 | -------------------------------------------------------------------------------- /contracts/ics101/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod approx_pow; 2 | pub mod contract; 3 | mod error; 4 | pub mod ibc; 5 | pub mod interchainswap_handler; 6 | pub mod market; 7 | mod math; 8 | pub mod msg; 9 | pub mod response; 10 | pub mod state; 11 | pub mod types; 12 | pub mod utils; 13 | 14 | pub use crate::error::ContractError; 15 | -------------------------------------------------------------------------------- /contracts/ics101/src/math.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::adjust_precision; 2 | use crate::{approx_pow::calculate_pow, types::WeightedAsset}; 3 | use cosmwasm_std::{Decimal, StdError, StdResult, Uint128}; 4 | 5 | // Referenced from Balancer Weighted pool implementation by Osmosis here - https://github.com/osmosis-labs/osmosis/blob/47a2366c5eeee474de9e1cb4777fab0ccfbb9592/x/gamm/pool-models/balancer/amm.go#L94 6 | // solveConstantFunctionInvariant solves the constant function of an AMM 7 | // https://github.com/dexter-zone/dexter_core/tree/main/contracts/pools/weighted_pool 8 | // that determines the relationship between the differences of two sides 9 | // of assets inside the pool. 10 | // -------------------------- 11 | // For fixed balanceXBefore, balanceXAfter, weightX, balanceY, weightY, 12 | // we could deduce the balanceYDelta, calculated by: 13 | // balanceYDelta = balanceY * (1 - (balanceXBefore/balanceXAfter)^(weightX/weightY)) 14 | // balanceYDelta is positive when the balance liquidity decreases. 15 | // balanceYDelta is negative when the balance liquidity increases. 16 | pub fn solve_constant_function_invariant( 17 | token_balance_fixed_before: Decimal, 18 | token_balance_fixed_after: Decimal, 19 | token_weight_fixed: Decimal, 20 | token_balance_unknown_before: Decimal, 21 | token_weight_unknown: Decimal, 22 | ) -> StdResult { 23 | // weight_ratio = (weightX/weightY) 24 | let weight_ratio = token_weight_fixed 25 | .checked_div(token_weight_unknown) 26 | .map_err(|e| StdError::generic_err(e.to_string()))?; 27 | 28 | // y = balanceXBefore/balanceXAfter 29 | let y = token_balance_fixed_before 30 | .checked_div(token_balance_fixed_after) 31 | .map_err(|e| StdError::generic_err(e.to_string()))?; 32 | 33 | // amount_y = balanceY * (1 - (y ^ weight_ratio)) 34 | let y_to_weight_ratio = calculate_pow(y, weight_ratio, None)?; 35 | // Decimal is an unsigned so always return abs value 36 | let paranthetical = if y_to_weight_ratio <= Decimal::one() { 37 | Decimal::one().checked_sub(y_to_weight_ratio)? 38 | } else { 39 | y_to_weight_ratio.checked_sub(Decimal::one())? 40 | }; 41 | 42 | let amount_y = token_balance_unknown_before.checked_mul(paranthetical)?; 43 | Ok(amount_y) 44 | } 45 | 46 | /// ## Description - Inspired from Osmosis implementation here - https://github.com/osmosis-labs/osmosis/blob/main/x/gamm/pool-models/balancer/amm.go#L116 47 | /// Calculates the amount of LP shares to be minted for Single asset joins. 48 | pub fn calc_minted_shares_given_single_asset_in( 49 | token_amount_in: Uint128, 50 | in_precision: u32, 51 | asset_weight_and_balance: &WeightedAsset, 52 | total_shares: Uint128, 53 | ) -> StdResult { 54 | let in_decimal = Decimal::from_atomics(token_amount_in, in_precision).unwrap(); 55 | let balance_decimal = 56 | Decimal::from_atomics(asset_weight_and_balance.asset.amount, in_precision).unwrap(); 57 | 58 | // To figure out the number of shares we add, first notice that we can treat 59 | // the number of shares as linearly related to the `k` value function. This is due to the normalization. 60 | // e.g, if x^.5 y^.5 = k, then we `n` x the liquidity to `(nx)^.5 (ny)^.5 = nk = k'` 61 | // --------- 62 | // We generalize this linear relation to do the liquidity add for the not-all-asset case. 63 | // Suppose we increase the supply of x by x', so we want to solve for `k'/k`. 64 | // This is `(x + x')^{weight} * old_terms / (x^{weight} * old_terms) = (x + x')^{weight} / (x^{weight})` 65 | // The number of new shares we need to make is then `old_shares * ((k'/k) - 1)` 66 | let pool_amount_out = solve_constant_function_invariant( 67 | balance_decimal + in_decimal, 68 | balance_decimal, 69 | asset_weight_and_balance.weight, 70 | Decimal::from_atomics(total_shares, Decimal::DECIMAL_PLACES).unwrap(), 71 | Decimal::one(), 72 | )?; 73 | let pool_amount_out_adj = adjust_precision( 74 | pool_amount_out.atomics(), 75 | pool_amount_out.decimal_places() as u8, 76 | Decimal::DECIMAL_PLACES as u8, 77 | )?; 78 | 79 | Ok(pool_amount_out_adj) 80 | } 81 | 82 | #[cfg(test)] 83 | mod tests { 84 | use std::str::FromStr; 85 | 86 | use super::*; 87 | #[test] 88 | fn test_solve_constant_function_invariant() { 89 | // Define some example inputs for the function 90 | let token_balance_fixed_before = Decimal::from_str("500000000000").unwrap(); 91 | let token_balance_fixed_after = Decimal::from_str("530000000000").unwrap(); 92 | let token_weight_fixed = Decimal::from_str("0.5").unwrap(); 93 | let token_balance_unknown_before = Decimal::from_str("500000000000").unwrap(); 94 | let token_weight_unknown = Decimal::from_str("0.5").unwrap(); 95 | 96 | // Call the function with the example inputs 97 | let result = solve_constant_function_invariant( 98 | token_balance_fixed_before, 99 | token_balance_fixed_after, 100 | token_weight_fixed, 101 | token_balance_unknown_before, 102 | token_weight_unknown, 103 | ); 104 | 105 | // let amount_dec = Decimal::from_ratio(2000u128, Uint128::one()); 106 | // let fee_rate_dec = Decimal::from_ratio(25u128, Uint128::new(10000)); 107 | // let fees = amount_dec * fee_rate_dec; 108 | // let amount_minus_fees = amount_dec - fees; 109 | // assert_eq!(amount_minus_fees, fees); 110 | // Assert the result is as expected 111 | assert!(result.is_ok()); 112 | let amount_y = result.unwrap(); 113 | let res = adjust_precision(amount_y.to_uint_floor(), 12, 6).unwrap(); 114 | assert_eq!(res, Uint128::from(28301u128)); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /contracts/ics101/src/state.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use cosmwasm_std::IbcEndpoint; 5 | use cw_storage_plus::{Item, Map}; 6 | 7 | use crate::{market::InterchainLiquidityPool, types::MultiAssetDepositOrder}; 8 | 9 | pub const CHANNEL_INFO: Map<&str, ChannelInfo> = Map::new("channel_info"); 10 | 11 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 12 | pub struct ChannelInfo { 13 | /// id of this channel 14 | pub id: String, 15 | /// the remote channel/port we connect to 16 | pub counterparty_endpoint: IbcEndpoint, 17 | /// the connection this exists on (you can use to query client/consensus info) 18 | pub connection_id: String, 19 | } 20 | 21 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 22 | pub struct Config { 23 | // Counter to keep track of multiassetdeposit orders 24 | pub counter: u64, 25 | // Token code id (Cw20) 26 | pub token_code_id: u64, 27 | // Admin address 28 | pub admin: String, 29 | // Router address 30 | pub router: String, 31 | } 32 | 33 | // Each pool has it's pool token (cw20) 34 | // Map pool-id -> pool token address 35 | pub const POOL_TOKENS_LIST: Map<&str, String> = Map::new("pool_tokens_list"); 36 | 37 | pub const CONFIG: Item = Item::new("config"); 38 | 39 | pub const TEMP: Item = Item::new("temp"); 40 | 41 | pub const POOLS: Map<&str, InterchainLiquidityPool> = Map::new("pools"); 42 | 43 | // Map from key (pool_id + "-" + order_id) to value multi asset orders 44 | pub const MULTI_ASSET_DEPOSIT_ORDERS: Map = 45 | Map::new("multi_asset_deposit_orders"); 46 | 47 | // Map from key (source_makers + "-" + pool_id) 48 | pub const ACTIVE_ORDERS: Map = Map::new("active_order"); 49 | 50 | // Map from pool_id to contract address 51 | pub const LOG_VOLUME: Map = Map::new("log_volume"); 52 | 53 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 54 | #[serde(rename_all = "SCREAMING_SNAKE_CASE")] 55 | pub enum Status { 56 | Initial, // initialed on maker chain 57 | Sync, // synced to the taker chain 58 | Cancel, // canceled 59 | Complete, // completed 60 | } 61 | -------------------------------------------------------------------------------- /contracts/ics101/src/types.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use cosmwasm_std::{Binary, Coin, Decimal, Uint128}; 5 | 6 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 7 | pub struct StateChange { 8 | #[serde(rename = "In")] 9 | pub in_tokens: Option>, 10 | #[serde(rename = "Out")] 11 | pub out_tokens: Option>, 12 | #[serde(rename = "PoolTokens")] 13 | pub pool_tokens: Option>, 14 | #[serde(rename = "PoolId")] 15 | pub pool_id: Option, 16 | #[serde(rename = "MultiDepositOrderId")] 17 | pub multi_deposit_order_id: Option, 18 | #[serde(rename = "SourceChainId")] 19 | pub source_chain_id: Option, 20 | #[serde(rename = "Shares")] 21 | pub shares: Option, 22 | } 23 | 24 | #[derive(Serialize, Deserialize)] 25 | pub struct Forward { 26 | pub port: String, 27 | pub channel: String, 28 | pub timeout: String, 29 | pub retries: i32, 30 | #[serde(skip_serializing_if = "Option::is_none")] // This line is to skip serialization if next is None 31 | pub next: Option, // Optional because it seems to be a comment in your example 32 | } 33 | 34 | #[derive(Serialize, Deserialize)] 35 | pub struct Memo { 36 | pub forward: Forward, 37 | } 38 | 39 | 40 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 41 | pub struct InterchainSwapPacketData { 42 | #[serde(rename = "Type")] 43 | pub r#type: InterchainMessageType, 44 | #[serde(rename = "Data")] 45 | pub data: Binary, 46 | #[serde(rename = "StateChange")] 47 | pub state_change: Option, 48 | #[serde(rename = "Memo")] 49 | pub memo: Option 50 | } 51 | 52 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 53 | pub enum InterchainMessageType { 54 | #[serde(rename = "UNSPECIFIED")] 55 | Unspecified = 0, 56 | #[serde(rename = "MAKE_POOL")] 57 | MakePool = 1, 58 | #[serde(rename = "TAKE_POOL")] 59 | TakePool = 2, 60 | #[serde(rename = "CANCEL_POOL")] 61 | CancelPool = 3, 62 | #[serde(rename = "SINGLE_ASSET_DEPOSIT")] 63 | SingleAssetDeposit = 4, 64 | #[serde(rename = "MAKE_MULTI_DEPOSIT")] 65 | MakeMultiDeposit = 5, 66 | #[serde(rename = "CANCEL_MULTI_DEPOSIT")] 67 | CancelMultiDeposit = 6, 68 | #[serde(rename = "TAKE_MULTI_DEPOSIT")] 69 | TakeMultiDeposit = 7, 70 | #[serde(rename = "MULTI_WITHDRAW")] 71 | MultiWithdraw = 8, 72 | #[serde(rename = "LEFT_SWAP")] 73 | LeftSwap = 9, 74 | #[serde(rename = "RIGHT_SWAP")] 75 | RightSwap = 10, 76 | } 77 | 78 | pub const MULTI_DEPOSIT_PENDING_LIMIT: u64 = 10; 79 | 80 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 81 | #[serde(rename_all = "SCREAMING_SNAKE_CASE")] 82 | pub enum OrderStatus { 83 | Pending = 0, 84 | Complete = 1, 85 | Cancelled = 2, 86 | } 87 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 88 | #[serde(rename_all = "camelCase")] 89 | pub struct MultiAssetDepositOrder { 90 | pub id: String, 91 | pub pool_id: String, 92 | pub chain_id: String, 93 | pub source_maker: String, 94 | pub destination_taker: String, 95 | pub deposits: Vec, 96 | //pub pool_tokens: Vec, 97 | pub status: OrderStatus, 98 | pub created_at: u64, 99 | } 100 | 101 | /// ## Description - This struct describes a asset (native or CW20) and its normalized weight 102 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 103 | #[serde(rename_all = "camelCase")] 104 | pub struct WeightedAsset { 105 | /// Information about an asset stored in a [`Asset`] struct 106 | pub asset: Coin, 107 | /// The weight of the asset 108 | pub weight: Decimal, 109 | } 110 | -------------------------------------------------------------------------------- /contracts/lp-staking/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideprotocol/mesh-liquidity-wasm/6a7f5f1f7cd2344311f6bfe17ac0be41bfe782a2/contracts/lp-staking/.DS_Store -------------------------------------------------------------------------------- /contracts/lp-staking/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lp-staking" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "Lock LP tokens to receive veToken" 6 | license = "Apache-2.0" 7 | 8 | [lib] 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [features] 12 | backtraces = ["cosmwasm-std/backtraces"] 13 | # use library feature to disable all instantiate/execute/query exports 14 | library = [] 15 | 16 | [dependencies] 17 | cw-utils = { version = "1.0.1" } 18 | cw2 = { version = "1.0.1" } 19 | cw20 = { version = "1.0.1" } 20 | cosmwasm-std = { version = "1.0.0-beta3", features = ["stargate"] } 21 | cw-storage-plus = { version = "1.0.1" } 22 | schemars = "0.8.1" 23 | serde = { version = "1.0.103", default-features = false, features = ["derive"] } 24 | thiserror = { version = "1.0.23" } 25 | hex = "0.3.1" 26 | sha2 = "0.8.0" 27 | 28 | [dev-dependencies] 29 | cosmwasm-schema = { version = "1.0.0-beta3" } -------------------------------------------------------------------------------- /contracts/lp-staking/README.md: -------------------------------------------------------------------------------- 1 | ## LP token staking contracts 2 | - This contract accepts a specified cw20 token. 3 | - User can specify lock period for the lp token a.k.a cw20 token. 4 | - Contract mints veToken according to lock period and amount of cw20 tokens. 5 | - This contract allocates token rewards (veToken) for various LP tokens and distributes them pro-rata to LP stakers. -------------------------------------------------------------------------------- /contracts/lp-staking/examples/schema.rs: -------------------------------------------------------------------------------- 1 | use std::env::current_dir; 2 | use std::fs::create_dir_all; 3 | 4 | use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; 5 | 6 | use lp_staking::msg::ExecuteMsg; 7 | use lp_staking::msg::InstantiateMsg; 8 | use lp_staking::msg::QueryMsg; 9 | 10 | fn main() { 11 | let mut out_dir = current_dir().unwrap(); 12 | out_dir.push("schema"); 13 | create_dir_all(&out_dir).unwrap(); 14 | remove_schemas(&out_dir).unwrap(); 15 | 16 | export_schema(&schema_for!(InstantiateMsg), &out_dir); 17 | export_schema(&schema_for!(ExecuteMsg), &out_dir); 18 | export_schema(&schema_for!(QueryMsg), &out_dir); 19 | } 20 | -------------------------------------------------------------------------------- /contracts/lp-staking/schema/execute_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "ExecuteMsg", 4 | "oneOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "LogObservation" 9 | ], 10 | "properties": { 11 | "LogObservation": { 12 | "type": "object", 13 | "required": [ 14 | "token1", 15 | "token2" 16 | ], 17 | "properties": { 18 | "token1": { 19 | "$ref": "#/definitions/Coin" 20 | }, 21 | "token2": { 22 | "$ref": "#/definitions/Coin" 23 | } 24 | } 25 | } 26 | }, 27 | "additionalProperties": false 28 | }, 29 | { 30 | "type": "object", 31 | "required": [ 32 | "SetContract" 33 | ], 34 | "properties": { 35 | "SetContract": { 36 | "type": "object", 37 | "required": [ 38 | "address" 39 | ], 40 | "properties": { 41 | "address": { 42 | "type": "string" 43 | } 44 | } 45 | } 46 | }, 47 | "additionalProperties": false 48 | } 49 | ], 50 | "definitions": { 51 | "Coin": { 52 | "type": "object", 53 | "required": [ 54 | "amount", 55 | "denom" 56 | ], 57 | "properties": { 58 | "amount": { 59 | "$ref": "#/definitions/Uint128" 60 | }, 61 | "denom": { 62 | "type": "string" 63 | } 64 | } 65 | }, 66 | "Uint128": { 67 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 68 | "type": "string" 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /contracts/lp-staking/schema/instantiate_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "type": "object", 5 | "required": [ 6 | "contract", 7 | "max_length" 8 | ], 9 | "properties": { 10 | "contract": { 11 | "type": "string" 12 | }, 13 | "max_length": { 14 | "type": "integer", 15 | "format": "uint64", 16 | "minimum": 0.0 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/lp-staking/schema/query_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "QueryMsg", 4 | "oneOf": [ 5 | { 6 | "description": "Returns volume between specific interval Returns total volume till latest timestamp", 7 | "type": "object", 8 | "required": [ 9 | "total_volume" 10 | ], 11 | "properties": { 12 | "total_volume": { 13 | "type": "object" 14 | } 15 | }, 16 | "additionalProperties": false 17 | }, 18 | { 19 | "description": "Returns total volume till given timestamp", 20 | "type": "object", 21 | "required": [ 22 | "total_volume_at" 23 | ], 24 | "properties": { 25 | "total_volume_at": { 26 | "type": "object", 27 | "required": [ 28 | "timestamp" 29 | ], 30 | "properties": { 31 | "timestamp": { 32 | "type": "integer", 33 | "format": "uint64", 34 | "minimum": 0.0 35 | } 36 | } 37 | } 38 | }, 39 | "additionalProperties": false 40 | }, 41 | { 42 | "description": "Returns contract address for which volume is tracked", 43 | "type": "object", 44 | "required": [ 45 | "contract" 46 | ], 47 | "properties": { 48 | "contract": { 49 | "type": "object" 50 | } 51 | }, 52 | "additionalProperties": false 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /contracts/lp-staking/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum Never {} 6 | 7 | #[derive(Error, Debug, PartialEq)] 8 | pub enum ContractError { 9 | #[error("{0}")] 10 | Std(#[from] StdError), 11 | 12 | #[error("Send some coins to create an atomic swap")] 13 | EmptyBalance {}, 14 | 15 | #[error("Cannot perform this action: Unauthorized")] 16 | Unauthorized, 17 | 18 | #[error("Duplicate pools in input")] 19 | PoolDuplicate, 20 | 21 | #[error("Pool not found")] 22 | PoolNotFound, 23 | 24 | #[error("Balance is too small, user doesn't have enough balance")] 25 | BalanceTooSmall, 26 | 27 | #[error("Order is not for this chain")] 28 | InvalidChain, 29 | 30 | #[error("Invalid sell token")] 31 | InvalidSellToken, 32 | 33 | #[error("Order has already been taken")] 34 | AlreadyTakenOrder, 35 | 36 | #[error("Invalid taker address")] 37 | InvalidTakerAddress, 38 | 39 | #[error("Invalid maker address")] 40 | InvalidMakerAddress, 41 | 42 | #[error("Invalid sender address")] 43 | InvalidSender, 44 | 45 | #[error("Invalid status")] 46 | InvalidStatus, 47 | 48 | #[error("Got a submessage reply with unknown id: {id}")] 49 | UnknownReplyId { id: u64 }, 50 | 51 | #[error("Only supports channel with ibc version ics100-1, got {version}")] 52 | InvalidIbcVersion { version: String }, 53 | 54 | #[error("Only supports unordered channel")] 55 | OnlyOrderedChannel {}, 56 | 57 | #[error("Only accepts tokens that originate on this chain, not native tokens of remote chain")] 58 | NoForeignTokens {}, 59 | 60 | #[error("Parsed port from denom ({port}) doesn't match packet")] 61 | FromOtherPort { port: String }, 62 | 63 | #[error("Parsed channel from denom ({channel}) doesn't match packet")] 64 | FromOtherChannel { channel: String }, 65 | 66 | #[error("Bid is not allowed for this order")] 67 | TakeBidNotAllowed, 68 | 69 | #[error("Bid already exist")] 70 | BidAlreadyExist, 71 | 72 | #[error("Bid doesn't exist")] 73 | BidDoesntExist, 74 | } 75 | -------------------------------------------------------------------------------- /contracts/lp-staking/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod error; 3 | pub mod msg; 4 | pub mod query; 5 | pub mod state; 6 | 7 | pub use crate::error::ContractError; 8 | pub use decimal_checked_ops::DecimalCheckedOps; 9 | 10 | mod decimal_checked_ops { 11 | use cosmwasm_std::{Decimal, Fraction, OverflowError, Uint128, Uint256}; 12 | use std::convert::TryInto; 13 | pub trait DecimalCheckedOps { 14 | fn checked_add(self, other: Decimal) -> Result; 15 | fn checked_mul_uint128(self, other: Uint128) -> Result; 16 | } 17 | 18 | impl DecimalCheckedOps for Decimal { 19 | fn checked_add(self, other: Decimal) -> Result { 20 | self.numerator() 21 | .checked_add(other.numerator()) 22 | .map(|_| self + other) 23 | } 24 | fn checked_mul_uint128(self, other: Uint128) -> Result { 25 | if self.is_zero() || other.is_zero() { 26 | return Ok(Uint128::zero()); 27 | } 28 | let multiply_ratio = 29 | other.full_mul(self.numerator()) / Uint256::from(self.denominator()); 30 | if multiply_ratio > Uint256::from(Uint128::MAX) { 31 | Err(OverflowError::new( 32 | cosmwasm_std::OverflowOperation::Mul, 33 | self, 34 | other, 35 | )) 36 | } else { 37 | Ok(multiply_ratio.try_into().unwrap()) 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/lp-staking/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cw20::Cw20ReceiveMsg; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use cosmwasm_std::{Addr, Uint128}; 6 | 7 | #[derive(Serialize, Deserialize, JsonSchema)] 8 | pub struct InstantiateMsg { 9 | pub reward_token: String, 10 | pub tokens_per_block: Uint128, 11 | pub total_alloc_point: Uint128, 12 | pub start_block: u64, 13 | } 14 | 15 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 16 | pub enum ExecuteMsg { 17 | SetupPools { 18 | pools: Vec<(String, Uint128)>, 19 | }, 20 | SetTokensPerBlock { 21 | /// The new amount of veToken to distribute per block 22 | amount: Uint128, 23 | }, 24 | ClaimRewards { 25 | /// the LP token contract address 26 | lp_tokens: Vec, 27 | 28 | receiver: String, 29 | }, 30 | /// Withdraw LP tokens from contract 31 | Withdraw { 32 | /// The address of the LP token to withdraw 33 | lp_token: String, 34 | /// The amount to withdraw 35 | amount: Uint128, 36 | }, 37 | UpdateConfig { 38 | // Reward token 39 | reward_token: String, 40 | }, 41 | Receive(Cw20ReceiveMsg), 42 | } 43 | 44 | /// This structure describes custom hooks for the CW20. 45 | #[derive(Serialize, Deserialize, PartialEq)] 46 | #[serde(rename_all = "snake_case")] 47 | pub enum Cw20HookMsg { 48 | /// Deposit performs a token deposit on behalf of the message sender. 49 | Deposit {}, 50 | /// DepositFor performs a token deposit on behalf of another address that's not the message sender. 51 | DepositFor { beneficiary: Addr }, 52 | } 53 | 54 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 55 | pub struct MigrateMsg {} 56 | 57 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 58 | #[serde(rename_all = "snake_case")] 59 | pub enum QueryMsg { 60 | /// Returns total volume till latest timestamp 61 | TotalVolume {}, 62 | /// Returns total volume till given timestamp 63 | TotalVolumeAt { timestamp: u64 }, 64 | /// Returns contract address for which volume is tracked 65 | Contract {}, 66 | } 67 | -------------------------------------------------------------------------------- /contracts/lp-staking/src/query.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{entry_point, to_binary, Binary, Deps, Env, StdResult}; 2 | 3 | use crate::msg::QueryMsg; 4 | 5 | // #[cfg_attr(not(feature = "library"), entry_point)] 6 | // pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { 7 | // match msg { 8 | // QueryMsg::Contract {} => to_binary(&query_contract(deps)?), 9 | // QueryMsg::TotalVolume {} => to_binary(&query_total_volume(deps, env)?), 10 | // QueryMsg::TotalVolumeAt { timestamp } => { 11 | // to_binary(&query_total_volume_at(deps, timestamp)?) 12 | // } //QueryMsg::VolumeInterval { start, end } => to_binary(&query_total_volume_interval(deps, start, end)?), 13 | // } 14 | // } 15 | -------------------------------------------------------------------------------- /contracts/lp-staking/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Addr, Decimal, Uint128, Uint64}; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use cw_storage_plus::{Item, Map}; 6 | 7 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 8 | pub struct Config { 9 | // admin 10 | pub admin: Addr, 11 | // Reward token 12 | pub reward_token: Addr, 13 | // distribution rate for reward token 14 | pub tokens_per_block: Uint128, 15 | // alloc points for a token 16 | pub total_alloc_point: Uint128, 17 | // start block 18 | pub start_block: u64, 19 | /// The list of active pools with allocation points 20 | pub active_pools: Vec<(Addr, Uint128)>, 21 | } 22 | 23 | /// This structure describes the main information of pool 24 | #[derive(Serialize, Deserialize, PartialEq)] 25 | pub struct PoolInfo { 26 | /// Accumulated amount of reward per share unit. Used for reward calculations 27 | pub last_reward_block: Uint64, 28 | pub reward_global_index: Decimal, 29 | pub has_asset_rewards: bool, 30 | pub total_supply: Uint128, 31 | } 32 | 33 | /// This structure stores the outstanding amount of token rewards that a user accured. 34 | #[derive(Serialize, Deserialize, PartialEq, Default)] 35 | pub struct UserInfo { 36 | /// The amount of LP tokens staked 37 | pub amount: Uint128, 38 | /// The amount of veToken rewards a user already received or is not eligible for; used for proper reward calculation 39 | pub reward_user_index: Decimal, 40 | } 41 | 42 | pub const CONFIG: Item = Item::new("config"); 43 | 44 | pub const POOL_INFO: Map<&Addr, PoolInfo> = Map::new("pool_info"); 45 | 46 | pub const USER_INFO: Map<&(Addr, Addr), UserInfo> = Map::new("user_info"); 47 | -------------------------------------------------------------------------------- /contracts/router/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sample-project" 3 | version = "1.0.0" 4 | authors = ["Udit Gulati"] 5 | edition = "2018" 6 | 7 | exclude = [ 8 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 9 | "contract.wasm", 10 | "hash.txt", 11 | ] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | crate-type = ["cdylib", "rlib"] 17 | 18 | [features] 19 | # for quicker tests, cargo test --lib 20 | # for more explicit tests, cargo test --features=backtraces 21 | backtraces = ["cosmwasm-std/backtraces"] 22 | library = [] 23 | 24 | [dependencies] 25 | cosmwasm-std = { version = "1.0.0-beta8", features = ["staking"] } 26 | schemars = "0.8.1" 27 | serde = { version = "1.0.103", default-features = false, features = ["derive"] } 28 | snafu = { version = "0.6.3" } 29 | thiserror = { version = "1.0.23" } 30 | cw-storage-plus = { version = "0.13.2" } 31 | 32 | [dev-dependencies] 33 | cosmwasm-schema = { version = "1.0.0-beta8" } 34 | -------------------------------------------------------------------------------- /contracts/router/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Simon Warta 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /contracts/router/README.md: -------------------------------------------------------------------------------- 1 | # CosmWasm Contracts Counter Example 2 | 3 | This template contains simple counter contracts. 4 | The contract is created with a parameter for the initial count and allows subsequent incrementing. 5 | 6 | # Contract Functions 7 | - `Increment`: Any user can increment the current count by 1. 8 | - `Reset`: Only the owner can reset the count to a specific number. 9 | - `get_count`: Any user can use this function to see current counter value. 10 | 11 | # Compiling contracts 12 | 13 | Use this command to compile your contracts: 14 | `junokit compile` 15 | 16 | # Run script 17 | 18 | `junokit run scripts/sample-script.js` 19 | 20 | # Deploying contracts 21 | 22 | In `scripts` folder: 23 | 24 | First of all you need to create an instance of your contract using contract name. 25 | ```ts 26 | const contract = new Contract('sample-project'); 27 | 28 | // To deploy your contract 29 | const deploy_response = await contract.deploy(account); 30 | 31 | // To initialize your contract 32 | await contract.instantiate({"count": 102}, "deploy test", account); 33 | ``` 34 | 35 | Note: You can check out your contract information in `deploy_response`. 36 | 37 | # Interact with contracts 38 | 39 | `junokit` will load functions using schema, you can call contract functions using `contract.tx`(to execute transactions) and `contract.query`(to query from contract) 40 | ```js 41 | // To interact with your contract 42 | // Execute contract function 43 | await contract.increment(account); 44 | 45 | // View count in contract 46 | await contract.get_count(); 47 | ``` 48 | -------------------------------------------------------------------------------- /contracts/router/examples/schema.rs: -------------------------------------------------------------------------------- 1 | use std::env::current_dir; 2 | use std::fs::create_dir_all; 3 | 4 | use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; 5 | 6 | 7 | use sample_project::{ 8 | 9 | ExecuteMsg,InstantiateMsg, QueryMsg, CountResponse, Constants, 10 | }; 11 | 12 | fn main() { 13 | let mut out_dir = current_dir().unwrap(); 14 | out_dir.push("schema"); 15 | create_dir_all(&out_dir).unwrap(); 16 | remove_schemas(&out_dir).unwrap(); 17 | 18 | export_schema(&schema_for!(InstantiateMsg), &out_dir); 19 | export_schema(&schema_for!(ExecuteMsg), &out_dir); 20 | export_schema(&schema_for!(QueryMsg), &out_dir); 21 | export_schema(&schema_for!(Constants), &out_dir); 22 | export_schema(&schema_for!(CountResponse), &out_dir); 23 | } 24 | -------------------------------------------------------------------------------- /contracts/router/schema/constants.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Constants", 4 | "type": "object", 5 | "required": [ 6 | "owner" 7 | ], 8 | "properties": { 9 | "owner": { 10 | "type": "string" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /contracts/router/schema/count_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "CountResponse", 4 | "type": "object", 5 | "required": [ 6 | "count" 7 | ], 8 | "properties": { 9 | "count": { 10 | "type": "integer", 11 | "format": "int32" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/router/schema/execute_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "ExecuteMsg", 4 | "oneOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "MultiSwap" 9 | ], 10 | "properties": { 11 | "MultiSwap": { 12 | "type": "object", 13 | "required": [ 14 | "offer_amount", 15 | "requests" 16 | ], 17 | "properties": { 18 | "minimum_receive": { 19 | "anyOf": [ 20 | { 21 | "$ref": "#/definitions/Uint128" 22 | }, 23 | { 24 | "type": "null" 25 | } 26 | ] 27 | }, 28 | "offer_amount": { 29 | "$ref": "#/definitions/Uint128" 30 | }, 31 | "receiver": { 32 | "anyOf": [ 33 | { 34 | "$ref": "#/definitions/Addr" 35 | }, 36 | { 37 | "type": "null" 38 | } 39 | ] 40 | }, 41 | "requests": { 42 | "type": "array", 43 | "items": { 44 | "$ref": "#/definitions/SwapRequest" 45 | } 46 | } 47 | } 48 | } 49 | }, 50 | "additionalProperties": false 51 | }, 52 | { 53 | "type": "object", 54 | "required": [ 55 | "Callback" 56 | ], 57 | "properties": { 58 | "Callback": { 59 | "$ref": "#/definitions/CallbackMsg" 60 | } 61 | }, 62 | "additionalProperties": false 63 | } 64 | ], 65 | "definitions": { 66 | "Addr": { 67 | "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", 68 | "type": "string" 69 | }, 70 | "CallbackMsg": { 71 | "oneOf": [ 72 | { 73 | "type": "object", 74 | "required": [ 75 | "HopSwap" 76 | ], 77 | "properties": { 78 | "HopSwap": { 79 | "type": "object", 80 | "required": [ 81 | "minimum_receive", 82 | "offer_asset", 83 | "prev_ask_amount", 84 | "recipient", 85 | "requests" 86 | ], 87 | "properties": { 88 | "minimum_receive": { 89 | "$ref": "#/definitions/Uint128" 90 | }, 91 | "offer_asset": { 92 | "type": "string" 93 | }, 94 | "prev_ask_amount": { 95 | "$ref": "#/definitions/Uint128" 96 | }, 97 | "recipient": { 98 | "$ref": "#/definitions/Addr" 99 | }, 100 | "requests": { 101 | "type": "array", 102 | "items": { 103 | "$ref": "#/definitions/SwapRequest" 104 | } 105 | } 106 | } 107 | } 108 | }, 109 | "additionalProperties": false 110 | } 111 | ] 112 | }, 113 | "SwapRequest": { 114 | "type": "object", 115 | "required": [ 116 | "asset_in", 117 | "asset_out", 118 | "pool_id" 119 | ], 120 | "properties": { 121 | "asset_in": { 122 | "description": "The offer asset denom", 123 | "type": "string" 124 | }, 125 | "asset_out": { 126 | "description": "The ask asset denom", 127 | "type": "string" 128 | }, 129 | "pool_id": { 130 | "description": "Pool Id via which the swap is to be routed", 131 | "type": "string" 132 | } 133 | } 134 | }, 135 | "Uint128": { 136 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 137 | "type": "string" 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /contracts/router/schema/instantiate_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "type": "object" 5 | } 6 | -------------------------------------------------------------------------------- /contracts/router/schema/query_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "QueryMsg", 4 | "oneOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "get_count" 9 | ], 10 | "properties": { 11 | "get_count": { 12 | "type": "object" 13 | } 14 | }, 15 | "additionalProperties": false 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /contracts/router/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum ContractError { 6 | #[error("{0}")] 7 | Std(#[from] StdError), 8 | 9 | #[error("Error: {0}", msg)] 10 | InvalidMultihopSwapRequest { msg: String }, 11 | } 12 | -------------------------------------------------------------------------------- /contracts/router/src/interaction_gmm.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Coin, CosmosMsg, CustomMsg}; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] 6 | #[serde(rename_all = "snake_case")] 7 | /// A number of Custom messages that can call into the side bindings 8 | pub enum SideMsg { 9 | Swap { 10 | pool_id: String, 11 | token_in: Coin, 12 | token_out: Coin, 13 | slippage: String, 14 | }, 15 | } 16 | 17 | impl SideMsg {} 18 | 19 | impl From for CosmosMsg { 20 | fn from(msg: SideMsg) -> CosmosMsg { 21 | CosmosMsg::Custom(msg) 22 | } 23 | } 24 | 25 | impl CustomMsg for SideMsg {} -------------------------------------------------------------------------------- /contracts/router/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod error; 3 | mod msg; 4 | mod state; 5 | mod query; 6 | mod querier; 7 | mod interaction_gmm; 8 | 9 | pub use msg::{ 10 | ExecuteMsg,InstantiateMsg, QueryMsg, CountResponse, 11 | }; 12 | pub use state::Constants; 13 | -------------------------------------------------------------------------------- /contracts/router/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Addr, Coin, Uint128}; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 6 | pub struct InstantiateMsg { 7 | } 8 | 9 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 10 | pub enum ExecuteMsg { 11 | MultiSwap { 12 | requests: Vec, 13 | offer_amount: Uint128, 14 | receiver: Option, 15 | minimum_receive: Option, 16 | }, 17 | Callback(CallbackMsg) 18 | } 19 | 20 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 21 | pub enum CallbackMsg { 22 | HopSwap { 23 | requests: Vec, 24 | offer_asset: String, 25 | prev_ask_amount: Uint128, 26 | recipient: Addr, 27 | minimum_receive: Uint128, 28 | }, 29 | } 30 | 31 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 32 | #[serde(rename_all = "snake_case")] 33 | pub struct SwapRequest { 34 | /// Pool Id via which the swap is to be routed 35 | pub pool_id: String, 36 | /// The offer asset denom 37 | pub asset_in: String, 38 | /// The ask asset denom 39 | pub asset_out: String, 40 | /// Contract address, if interchain request 41 | pub contract_address: Option, 42 | } 43 | 44 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 45 | pub enum InterchainExecuteMsg { 46 | Swap { 47 | #[serde(rename = "swapType")] 48 | swap_type: SwapMsgType, 49 | sender: String, 50 | #[serde(rename = "poolId")] 51 | pool_id: String, 52 | #[serde(rename = "tokenIn")] 53 | token_in: Coin, 54 | #[serde(rename = "tokenOut")] 55 | token_out: Coin, 56 | slippage: u64, 57 | recipient: String, 58 | #[serde(rename = "timeoutHeight")] 59 | timeout_height: u64, 60 | #[serde(rename = "timeoutTimestamp")] 61 | timeout_timestamp: u64, 62 | route: Option, 63 | } 64 | } 65 | 66 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 67 | pub struct SwapRoute { 68 | pub requests: Vec, 69 | pub minimum_receive: Option, 70 | } 71 | 72 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 73 | pub enum SwapMsgType { 74 | LEFT = 0, 75 | RIGHT = 1, 76 | } 77 | 78 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 79 | #[serde(rename_all = "snake_case")] 80 | pub enum QueryMsg { 81 | // GetCount returns the current count as a json-encoded number 82 | GetCount {}, 83 | } 84 | 85 | // We define a custom struct for each query response 86 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 87 | pub struct CountResponse { 88 | pub count: i32, 89 | } 90 | 91 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 92 | pub struct Params { 93 | pub pool_creation_fee: u64, 94 | } 95 | 96 | // We define a custom struct for each query response 97 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 98 | pub struct ParamResponse { 99 | pub params: Params, 100 | } 101 | -------------------------------------------------------------------------------- /contracts/router/src/querier.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{QuerierWrapper, StdResult}; 2 | 3 | use crate::msg::ParamResponse; 4 | use crate::query::SideQuery; 5 | 6 | pub struct SideQuerier<'a> { 7 | querier: &'a QuerierWrapper<'a, SideQuery>, 8 | } 9 | 10 | impl<'a> SideQuerier<'a> { 11 | pub fn new(querier: &'a QuerierWrapper) -> Self { 12 | SideQuerier { querier } 13 | } 14 | 15 | // Gmm 16 | pub fn query_params(&self) -> StdResult { 17 | let request = SideQuery::Params { }; 18 | 19 | let res: ParamResponse = self.querier.query(&request.into())?; 20 | Ok(res) 21 | } 22 | } -------------------------------------------------------------------------------- /contracts/router/src/query.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::CustomQuery; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// SideQuery is an override of QueryRequest::Custom to access Side-specific modules 6 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] 7 | #[serde(rename_all = "snake_case")] 8 | pub enum SideQuery { 9 | // Exchange 10 | Params {}, 11 | Pool {}, 12 | // TODO: Add more queries 13 | // Pools { 14 | // } 15 | } 16 | 17 | impl CustomQuery for SideQuery {} -------------------------------------------------------------------------------- /contracts/router/src/state.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use cw_storage_plus::Item; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | pub const CONSTANTS: Item = Item::new("constants"); 6 | 7 | #[derive(Serialize, Debug, Deserialize, Clone, PartialEq, JsonSchema)] 8 | pub struct Constants { 9 | pub owner: String 10 | } 11 | 12 | -------------------------------------------------------------------------------- /contracts/staking_contract/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "staking-contract" 3 | version = "0.0.1" 4 | authors = ["Amit Yadav"] 5 | edition = "2018" 6 | 7 | exclude = [ 8 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 9 | "contract.wasm", 10 | "hash.txt", 11 | ] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | crate-type = ["cdylib", "rlib"] 17 | 18 | [features] 19 | # for quicker tests, cargo test --lib 20 | # for more explicit tests, cargo test --features=backtraces 21 | backtraces = ["cosmwasm-std/backtraces"] 22 | library = [] 23 | 24 | [dependencies] 25 | cosmwasm-std = { version = "1.1", features = ["staking","stargate"] } 26 | schemars = "0.8.1" 27 | serde = { version = "1.0.103", default-features = false, features = ["derive"] } 28 | snafu = { version = "0.6.3" } 29 | rand = "0.5.0" 30 | cosmwasm-bignumber = "2.2.0" 31 | thiserror = { version = "1.0.23" } 32 | cw-controllers = { version = "0.15" } 33 | cw-storage-plus = { version = "0.15" } 34 | cw2 = { version = "0.15" } 35 | cw20 = { version = "0.15" } 36 | rust_decimal = { version = "1.7.0", default-features = false, features = ["serde"]} 37 | 38 | [dev-dependencies] 39 | cosmwasm-schema = { version = "1.1" } 40 | -------------------------------------------------------------------------------- /contracts/staking_contract/README.md: -------------------------------------------------------------------------------- 1 | # LSD contract for SIDE token 2 | 3 | ## Overview 4 | 5 | LSD will provide the layer of smart contracts between delegator and validator which auto-compounds the rewards after a fixed interval without requirement of delegator intervention and provide a derivative sdSIDE which can be used in DeFi. 6 | This uses accuring rewards model i.e all the rewards are accumulated in sdSIDE itself, and sdSIDE value increases over time. Increase in value depends on staking reward rate of chain. 7 | 8 | ## Features 9 | 10 | - Ability to convert staked SIDE into sdSIDE is done by issuing a staking derivative token which represents the user’s delegator’s stake and rewards accumulated. 11 | - This staking derivative token can be swapped with sdSIDE using a DEX supporting such pairs or used in lending. 12 | 13 | ## Stake-Unstake flow 14 | 15 | - User stakes with SIDE and receives sdSIDE in exchange. 16 | - Manager or User calls `claim_and_stake`.(This will claim rewards from validators and restake them). 17 | - User unstakes (sends sdSIDE in contract and gets SIDE coins). 18 | - Manager calls Advance Window(undelegation from validators start here). 19 | - User claims the unbonded amount (after undelegation is completed). 20 | 21 | ## Exchange rate 22 | 23 | - Exchange_rate function calculates 24 | 25 | - total sdSIDE tokens in the supply and 26 | - total on chain SIDE balance along with rewards (deposited SIDE in contract + rewards accumulated from validators) 27 | - returns ratio of total sdSIDE to total on chain SIDE. 28 | ```rust 29 | let total_on_chain = get_onchain_balance_with_rewards(querier, store, &contract_address)?; 30 | let tokens = query_total_supply(querier, &sdSIDE_token)? 31 | .u128() 32 | .saturating_sub(state.sdSIDE_to_burn.u128()); 33 | let exchange_rate = _calc_exchange_rate(total_on_chain, tokens)?; 34 | Ok(exchange_rate) 35 | ``` 36 | 37 | ## Stake from Contract to the validators - Claim_and_stake call 38 | 39 | - After users deposit in the contract, Contract manager or user calls ClaimAndStake which triggers claim_and_stake function. 40 | - `claim_and_stake` function collects the rewards accumulated to the validators and 41 | new deposits in the contract and stake the collected amount to the validators. 42 | - `claim_and_stake` is called every x hours to delegate the accumulated rewards and this way users get rid of staking their rewards again and again. 43 | - Staking is done in such a way that keeps difference between staked amount on each validators minimum. 44 | ```rust 45 | let slashing_amount = (state.sdSIDE_backing) 46 | .saturating_sub(state.to_deposit + Uint128::from(validator_set.total_staked())); 47 | state.sdSIDE_backing = state 48 | .sdSIDE_backing 49 | .saturating_sub(Uint128::from(slashing_amount.u128())); 50 | 51 | // claim rewards 52 | messages.append(&mut validator_set.withdraw_rewards_messages()); 53 | 54 | let reward_amount = 55 | get_rewards(deps.storage, deps.querier, &env.contract.address).unwrap_or_default(); 56 | 57 | let fee = calc_fee(reward_amount, config.dev_fee); 58 | ``` 59 | 60 | ## Unbonding 61 | 62 | - When user sends sdSIDE tokens to withdraw, Receive call automatically gets triggered which triggers `try_withdraw` function. 63 | 64 | To keep track of unbonding SIDE from validators we are using a data structure named `window_manager`. The schematic is as follows : 65 | 66 | ```rust 67 | `window_manager` 68 | |-- queue_window 69 | |-- ongoing_window (It is a vecDeque of type ongoingWithWithdrawWindows) 70 | 71 | `queue_window` 72 | |-- total_sdSIDE amount (total sdSIDE in the queue for unbonding) 73 | |-- sdSIDE_users_amount (hashmap which contains sdSIDE amount per user) 74 | 75 | `ongoingWithWithdrawWindows` 76 | |-- total_sdSIDE amount (total sdSIDE) 77 | |-- total_SIDE amount (corresponding total SIDE for unbonding) 78 | |-- side_users_amount (hashmap which contains SIDE corresponding each user) 79 | ``` 80 | 81 | - On receiving sdSIDE tokens, `try_withdraw` function only updates queue_window's data of window_manager. 82 | - Now, to unbond SIDE from validators, contract manager advances the window and call `AdvanceWindow` which triggers `advance_window_1` function. 83 | ```rust 84 | let mut validator_set = VALIDATOR_SET.load(deps.storage)?; 85 | let mut window_manager = WINDOW_MANANGER.load(deps.storage)?; 86 | 87 | // Store user's sdSIDE amount in active window (WithdrawWindow) 88 | window_manager.add_user_amount_to_active_window( 89 | deps.storage, 90 | Addr::unchecked(_cw20_msg.sender.clone()), 91 | _cw20_msg.amount, 92 | )?; 93 | let total_sdside_amount = window_manager.queue_window.total_sdSIDE; 94 | let user_sdside_amount = window_manager.get_user_sdSIDE_in_active_window( 95 | deps.storage, 96 | Addr::unchecked(_cw20_msg.sender.clone()), 97 | )?; 98 | 99 | ``` 100 | 101 | ## Batched Unbonding Requests 102 | - Due to a limit on simultaneous unbonding requests per account, unbonding requests are batched to ensure efficient processing. 103 | - A maximum of 7 simultaneous unbonding requests is allowed for a single account. 104 | - Unbonding requests are accumulated until a batch is formed. 105 | Batch time is calculated as (unbonding period) / 7 to comply with the simultaneous unbonding request limit. 106 | -------------------------------------------------------------------------------- /contracts/staking_contract/examples/schema.rs: -------------------------------------------------------------------------------- 1 | use std::env::current_dir; 2 | use std::fs::create_dir_all; 3 | 4 | use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; 5 | 6 | use staking_contract::msg::{ 7 | ExecuteMsg, InstantiateMsg, PendingClaimsResponse, QueryMsg, QueryResponse, 8 | TopValidatorsResponse, 9 | }; 10 | 11 | fn main() { 12 | let mut out_dir = current_dir().unwrap(); 13 | out_dir.push("schema"); 14 | create_dir_all(&out_dir).unwrap(); 15 | remove_schemas(&out_dir).unwrap(); 16 | 17 | export_schema(&schema_for!(InstantiateMsg), &out_dir); 18 | export_schema(&schema_for!(ExecuteMsg), &out_dir); 19 | export_schema(&schema_for!(QueryMsg), &out_dir); 20 | export_schema(&schema_for!(QueryResponse), &out_dir); 21 | export_schema(&schema_for!(TopValidatorsResponse), &out_dir); 22 | export_schema(&schema_for!(PendingClaimsResponse), &out_dir); 23 | } 24 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/admin.rs: -------------------------------------------------------------------------------- 1 | use crate::ContractError; 2 | use cosmwasm_std::{CosmosMsg, DepsMut, Env, MessageInfo, Response, StdError}; 3 | 4 | use crate::msg::ExecuteMsg; 5 | use crate::types::config::CONFIG; 6 | use crate::types::killswitch::KillSwitch; 7 | use crate::types::validator_set::VALIDATOR_SET; 8 | 9 | pub fn admin_commands( 10 | deps: DepsMut, 11 | _env: Env, 12 | info: MessageInfo, 13 | msg: ExecuteMsg, 14 | ) -> Result { 15 | let mut config = CONFIG.load(deps.storage)?; 16 | 17 | if info.sender != config.admin { 18 | return Err(ContractError::Std(StdError::generic_err( 19 | "Admin commands can only be ran from deployer address", 20 | ))); 21 | } 22 | 23 | match msg { 24 | ExecuteMsg::ChangeDevFee { 25 | dev_fee, 26 | dev_address, 27 | } => { 28 | if let Some(dev_fee) = dev_fee { 29 | config.dev_fee = dev_fee; 30 | } 31 | if let Some(dev_address) = dev_address { 32 | config.dev_address = dev_address; 33 | } 34 | CONFIG.save(deps.storage, &config)?; 35 | 36 | Ok(Response::new()) 37 | } 38 | 39 | ExecuteMsg::ChangeReferralContract { referral_contract } => { 40 | config.referral_contract = Some(referral_contract); 41 | CONFIG.save(deps.storage, &config)?; 42 | 43 | Ok(Response::new()) 44 | } 45 | 46 | ExecuteMsg::AddValidator { address } => { 47 | let mut messages: Vec = vec![]; 48 | let vals = deps.querier.query_all_validators()?; 49 | 50 | if !vals.iter().any(|v| v.address == address) { 51 | return Err(ContractError::Std(StdError::generic_err(format!( 52 | "{} is not in the current validator set", 53 | address 54 | )))); 55 | } 56 | 57 | let mut validator_set = VALIDATOR_SET.load(deps.storage)?; 58 | validator_set.add(address.to_string()); 59 | VALIDATOR_SET.save(deps.storage, &validator_set)?; 60 | 61 | Ok(Response::new().add_messages(messages)) 62 | } 63 | 64 | ExecuteMsg::KillSwitchOpenWithdraws {} => { 65 | config.kill_switch = KillSwitch::Open.into(); 66 | CONFIG.save(deps.storage, &config)?; 67 | 68 | Ok(Response::new()) 69 | } 70 | 71 | ExecuteMsg::ChangeOwner { new_owner } => { 72 | let mut config = CONFIG.load(deps.storage)?; 73 | config.admin = new_owner; 74 | CONFIG.save(deps.storage, &config)?; 75 | Ok(Response::new()) 76 | } 77 | 78 | _ => Err(ContractError::Std(StdError::generic_err( 79 | "Invalid message type".to_string(), 80 | ))), 81 | }; 82 | 83 | Ok(Response::new()) 84 | } 85 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/claim.rs: -------------------------------------------------------------------------------- 1 | use crate::types::config::CONFIG; 2 | use crate::types::withdraw_window::{USER_CLAIMABLE, USER_CLAIMABLE_AMOUNT}; 3 | use crate::ContractError; 4 | use crate::{msg::ExecuteMsg, state::STATE}; 5 | use cosmwasm_std::{ 6 | BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, StdError, Uint128, 7 | }; 8 | 9 | /** 10 | * Transfer matured SIDE from contract to user's wallet. 11 | */ 12 | pub fn claim( 13 | deps: DepsMut, 14 | _env: Env, 15 | _info: MessageInfo, 16 | _msg: ExecuteMsg, 17 | ) -> Result { 18 | let mut messages: Vec = vec![]; 19 | let mut state = STATE.load(deps.storage)?; 20 | let config = CONFIG.load(deps.storage)?; 21 | 22 | if config.paused == true { 23 | return Err(ContractError::Std(StdError::generic_err( 24 | "The contract is temporarily paused", 25 | ))); 26 | } 27 | 28 | let mut user_claimable = USER_CLAIMABLE.load(deps.storage)?; 29 | 30 | let side_amount: Uint128 = Uint128::from(0u128); 31 | if let Some(side_amount) = USER_CLAIMABLE_AMOUNT.may_load(deps.storage, &_info.sender)? { 32 | user_claimable.total_side -= side_amount; 33 | 34 | if side_amount > Uint128::from(0u128) { 35 | // If users wants side amount 36 | messages.push(CosmosMsg::Bank(BankMsg::Send { 37 | to_address: _info.sender.to_string(), 38 | amount: vec![Coin { 39 | denom: "uside".to_string(), 40 | amount: Uint128::from(side_amount), 41 | }], 42 | })); 43 | 44 | state.not_redeemed -= side_amount.clone(); 45 | } 46 | 47 | STATE.save(deps.storage, &state)?; 48 | USER_CLAIMABLE.save(deps.storage, &user_claimable)?; 49 | USER_CLAIMABLE_AMOUNT.remove(deps.storage, &_info.sender); 50 | } 51 | 52 | Ok(Response::new() 53 | .add_messages(messages) 54 | .add_attribute("action", "claim") 55 | .add_attribute("account", _info.sender) 56 | .add_attribute("amount", side_amount)) 57 | } 58 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/constants.rs: -------------------------------------------------------------------------------- 1 | pub const LSSIDE_SYMBOL: &str = "lsside"; 2 | pub const NATIVE_TOKEN_SYMBOL: &str = "uside"; 3 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | use cw_controllers::{AdminError, HookError}; 5 | 6 | #[derive(Error, Debug, PartialEq)] 7 | pub enum ContractError { 8 | #[error("{0}")] 9 | Std(#[from] StdError), 10 | 11 | #[error("{0}")] 12 | Admin(#[from] AdminError), 13 | 14 | #[error("{0}")] 15 | Hook(#[from] HookError), 16 | 17 | #[error("Unauthorized")] 18 | Unauthorized {}, 19 | 20 | #[error("No claims that can be released currently")] 21 | NothingToClaim {}, 22 | 23 | #[error("Must send '{0}' to stake")] 24 | MissingDenom(String), 25 | 26 | #[error("Sent unsupported denoms, must send '{0}' to stake")] 27 | ExtraDenoms(String), 28 | 29 | #[error("Must send valid address to stake")] 30 | InvalidDenom(String), 31 | 32 | #[error("Missed address or denom")] 33 | MixedNativeAndCw20(String), 34 | 35 | #[error("No funds sent")] 36 | NoFunds {}, 37 | 38 | #[error("No data in ReceiveMsg")] 39 | NoData {}, 40 | } 41 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod admin; 2 | pub mod claim; 3 | pub mod constants; 4 | pub mod contract; 5 | pub mod deposit; 6 | pub mod error; 7 | pub mod msg; 8 | pub mod query; 9 | pub mod receive; 10 | pub mod staking; 11 | pub mod state; 12 | pub mod tokens; 13 | pub mod types; 14 | pub mod update; 15 | pub mod utils; 16 | pub mod window; 17 | pub mod withdraw; 18 | 19 | pub use crate::error::ContractError; 20 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Addr, Uint128, VoteOption}; 2 | use cw20::Cw20ReceiveMsg; 3 | use schemars::JsonSchema; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::{tokens::Contract, types::validator_set::ValidatorResponse}; 7 | 8 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 9 | pub struct InstantiateMsg { 10 | pub dev_address: Addr, 11 | pub dev_fee: Option, 12 | pub epoch_period: u64, 13 | pub underlying_coin_denom: String, 14 | pub unbonding_period: u64, 15 | pub reward_denom: String, 16 | } 17 | 18 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 19 | #[serde(rename_all = "snake_case")] 20 | pub enum ExecuteMsg { 21 | Stake { 22 | referral: u64, 23 | }, 24 | Claim {}, 25 | ClaimAndStake {}, 26 | 27 | UpdateLssideAddr { 28 | address: Addr, 29 | }, 30 | UpdateRewardsAddr { 31 | address: Addr, 32 | }, 33 | 34 | // // token interaction 35 | Receive(Cw20ReceiveMsg), 36 | 37 | AdvanceWindow {}, 38 | RebalanceSlash {}, 39 | 40 | PauseContract {}, 41 | UnpauseContract {}, 42 | 43 | // voting 44 | VoteOnChain { 45 | proposal: u64, 46 | vote: VoteOption, 47 | }, 48 | 49 | //Remove validator from set - redelegates all bonds to next available validator 50 | RemoveValidator { 51 | address: String, 52 | redelegate: Option, 53 | }, 54 | 55 | // // add a new validator to the set 56 | //address in Addr(if string then will have to validate it before changing to Addr) 57 | AddValidator { 58 | address: Addr, 59 | }, 60 | 61 | Redelegate { 62 | from: String, 63 | to: String, 64 | }, 65 | 66 | ChangeOwner { 67 | new_owner: Addr, 68 | }, 69 | // Unbond everything 70 | KillSwitchUnbond {}, 71 | 72 | // // open the floodgates 73 | KillSwitchOpenWithdraws {}, 74 | 75 | // TODO: Add tests for unbonding time. 76 | ChangeUnbondingTime { 77 | new_time: u64, 78 | }, 79 | 80 | ChangeDevFee { 81 | dev_fee: Option, 82 | dev_address: Option, 83 | }, 84 | ChangeReferralContract { 85 | referral_contract: Addr, 86 | }, 87 | } 88 | 89 | #[derive(Serialize, Deserialize, JsonSchema)] 90 | #[serde(rename_all = "snake_case")] 91 | pub enum AirdropMessage1 { 92 | Claim { 93 | stage: u8, 94 | amount: Uint128, 95 | proof: Vec, 96 | }, 97 | } 98 | 99 | #[derive(Serialize, Deserialize, JsonSchema)] 100 | #[serde(rename_all = "snake_case")] 101 | pub enum AirdropMessage2 { 102 | Claim { amount: Uint128, proof: Vec }, 103 | } 104 | 105 | #[derive(Serialize, Deserialize, JsonSchema)] 106 | #[serde(rename_all = "snake_case")] 107 | pub enum AirdropMessage3 { 108 | // TODO: add more variations here if found later 109 | Claim {}, 110 | } 111 | 112 | #[derive(Serialize, Deserialize, JsonSchema)] 113 | #[serde(rename_all = "snake_case")] 114 | pub enum RewardClaim { 115 | Claim { recipient: String }, 116 | } 117 | 118 | #[derive(Serialize, Deserialize, JsonSchema)] 119 | #[serde(rename_all = "snake_case")] 120 | pub enum ReferralMsg { 121 | Deposit { 122 | recipient: String, 123 | code: u64, 124 | amount: Uint128, 125 | }, 126 | Withdraw { 127 | recipient: String, 128 | amount: Uint128, 129 | }, 130 | } 131 | 132 | #[derive(Serialize, Deserialize, JsonSchema)] 133 | #[serde(rename_all = "snake_case")] 134 | pub enum QueryRewards { 135 | AccruedRewards { address: String }, 136 | } 137 | 138 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 139 | #[serde(rename_all = "snake_case")] 140 | pub enum QueryMsg { 141 | LssideExchangeRate {}, 142 | QueryDevFee {}, 143 | Info {}, 144 | Undelegations { address: Addr }, 145 | UserClaimable { address: Addr }, 146 | Window {}, 147 | ActiveUnbonding { address: Addr }, 148 | } 149 | 150 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 151 | pub struct MigrateMsg {} 152 | 153 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 154 | pub struct AccruedRewardsResponse { 155 | pub rewards: Uint128, 156 | } 157 | 158 | // used by receive cw20 159 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 160 | #[serde(rename_all = "snake_case")] 161 | pub enum Cw20HookMsg { 162 | Unbond {}, 163 | } 164 | 165 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 166 | pub struct TopValidatorsResponse { 167 | pub validators: Vec, 168 | } 169 | 170 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 171 | #[serde(rename_all = "snake_case")] 172 | pub enum QueryMsgValidator { 173 | GetValidators { top: i32, oth: i32, com: i32 }, 174 | } 175 | 176 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 177 | pub struct PendingClaimsResponse { 178 | pub window_id: u64, 179 | pub claim_time: u64, 180 | pub side_amount: Uint128, 181 | } 182 | 183 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] 184 | #[serde(rename_all = "snake_case")] 185 | pub enum QueryResponse { 186 | Info { 187 | admin: Addr, 188 | validator_set: Vec, 189 | total_staked: Uint128, 190 | to_deposit: Uint128, 191 | lsside_backing: Uint128, 192 | lsside_to_burn: Uint128, 193 | lsside_in_contract: Uint128, 194 | side_under_withdraw: Uint128, 195 | ls_side_token: Addr, 196 | kill_switch: u8, 197 | epoch_period: u64, 198 | unbonding_period: u64, 199 | underlying_coin_denom: String, 200 | reward_denom: String, 201 | dev_address: Addr, 202 | dev_fee: u64, 203 | }, 204 | PendingClaims { 205 | pending: Vec, 206 | }, 207 | ActiveUndelegation { 208 | lsside_amount: Uint128, 209 | }, 210 | TopValidators { 211 | validators: Vec, 212 | }, 213 | LssideExchangeRate { 214 | rate: String, 215 | denom: String, 216 | }, 217 | DevFee { 218 | fee: u64, 219 | address: Addr, 220 | }, 221 | Window { 222 | id: u64, 223 | time_to_close: u64, 224 | lsside_amount: Uint128, 225 | }, 226 | Unbonding { 227 | unbonding_amount: Uint128, 228 | }, 229 | Claimable { 230 | claimable_amount: Uint128, 231 | }, 232 | } 233 | 234 | #[derive(Serialize, Deserialize, JsonSchema)] 235 | #[serde(rename_all = "snake_case")] 236 | pub enum TokenHandleMessage { 237 | SetVotingContract { 238 | contract: Option, 239 | gov_token: bool, 240 | }, 241 | } 242 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/query.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{to_binary, Addr, Binary, Deps, QuerierWrapper, StdResult, Uint128}; 2 | 3 | use crate::msg::QueryResponse; 4 | use crate::staking::lsside_exchange_rate; 5 | use crate::state::STATE; 6 | use crate::types::config::CONFIG; 7 | use crate::types::validator_set::VALIDATOR_SET; 8 | use crate::types::window_manager::WINDOW_MANANGER; 9 | use crate::types::withdraw_window::{QUEUE_WINDOW_AMOUNT, USER_CLAIMABLE_AMOUNT}; 10 | 11 | pub fn query_info(deps: Deps) -> StdResult { 12 | let config = CONFIG.load(deps.storage)?; 13 | let state = STATE.load(deps.storage)?; 14 | let validator_set = VALIDATOR_SET.load(deps.storage)?; 15 | 16 | to_binary(&QueryResponse::Info { 17 | admin: config.admin, 18 | validator_set: validator_set.to_query_response(), 19 | total_staked: Uint128::from(validator_set.total_staked()), 20 | to_deposit: Uint128::from(state.to_deposit), 21 | lsside_backing: Uint128::from(state.lsside_backing), 22 | lsside_to_burn: Uint128::from(state.lsside_to_burn), 23 | lsside_in_contract: Uint128::from(state.lsside_under_withdraw), 24 | side_under_withdraw: Uint128::from(state.side_under_withdraw), 25 | ls_side_token: config.ls_side_token.unwrap_or(Addr::unchecked("")), 26 | epoch_period: config.epoch_period, 27 | unbonding_period: config.unbonding_period, 28 | underlying_coin_denom: config.underlying_coin_denom, 29 | reward_denom: config.reward_denom, 30 | dev_address: config.dev_address, 31 | dev_fee: config.dev_fee, 32 | kill_switch: config.kill_switch, 33 | }) 34 | } 35 | 36 | pub fn query_dev_fee(deps: Deps) -> StdResult { 37 | let config = CONFIG.load(deps.storage)?; 38 | 39 | to_binary(&QueryResponse::DevFee { 40 | fee: config.dev_fee, 41 | address: config.dev_address, 42 | }) 43 | } 44 | 45 | pub fn query_lsside_exchange_rate(deps: Deps) -> StdResult { 46 | let ratio = lsside_exchange_rate(deps.storage, deps.querier)?; 47 | 48 | let rate = if ratio.is_zero() { 49 | "1".to_string() 50 | } else { 51 | ratio.to_string() 52 | }; 53 | 54 | to_binary(&QueryResponse::LssideExchangeRate { 55 | rate, 56 | denom: "uside".to_string(), 57 | }) 58 | } 59 | 60 | pub fn query_pending_claims(deps: Deps, address: Addr) -> StdResult { 61 | let window_manager = WINDOW_MANANGER.load(deps.storage)?; 62 | let pending_withdraws = window_manager.get_user_pending_withdraws(deps.storage, address)?; 63 | 64 | to_binary(&QueryResponse::PendingClaims { 65 | pending: pending_withdraws, 66 | }) 67 | } 68 | 69 | pub fn query_current_window(deps: Deps) -> StdResult { 70 | let manager = WINDOW_MANANGER.load(deps.storage)?; 71 | 72 | to_binary(&QueryResponse::Window { 73 | id: manager.queue_window.id, 74 | time_to_close: manager.time_to_close_window, 75 | lsside_amount: manager.queue_window.total_lsside, 76 | }) 77 | } 78 | 79 | // query active undelegations 80 | pub fn query_active_undelegation(_deps: Deps, address: Addr) -> StdResult { 81 | let mut lsside_amount = Uint128::from(0u128); 82 | 83 | if let Some(lsside_value) = QUEUE_WINDOW_AMOUNT.may_load(_deps.storage, &address)? { 84 | lsside_amount = lsside_value.clone(); 85 | } 86 | 87 | to_binary(&QueryResponse::ActiveUndelegation { 88 | lsside_amount: lsside_amount, 89 | }) 90 | } 91 | 92 | pub fn query_user_claimable(deps: Deps, address: Addr) -> StdResult { 93 | let mut user_side_amount = Uint128::from(0u128); 94 | if let Some(user_claimable) = USER_CLAIMABLE_AMOUNT.may_load(deps.storage, &address)? { 95 | user_side_amount = user_claimable; 96 | } 97 | 98 | to_binary(&QueryResponse::Claimable { 99 | claimable_amount: user_side_amount, 100 | }) 101 | } 102 | 103 | pub fn query_delegation( 104 | querier: &QuerierWrapper, 105 | validator: &str, 106 | contract_addr: &Addr, 107 | ) -> StdResult { 108 | Ok(querier 109 | .query_delegation(contract_addr, validator)? 110 | .map(|fd| fd.amount.amount.u128()) 111 | .unwrap_or(0)) 112 | } 113 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/receive.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{from_binary, DepsMut, Env, MessageInfo, Response, StdError}; 2 | use cw20::Cw20ReceiveMsg; 3 | 4 | use crate::{msg::Cw20HookMsg, types::config::CONFIG, withdraw::try_withdraw, ContractError}; 5 | 6 | /** 7 | * Receive cw20 token (lsSIDE) and 8 | * apply action either convert or withdraw 9 | * read from msg parameters. 10 | */ 11 | pub fn try_receive_cw20( 12 | deps: DepsMut, 13 | env: Env, 14 | info: MessageInfo, 15 | _cw20_msg: Cw20ReceiveMsg, 16 | ) -> Result { 17 | let config = CONFIG.load(deps.storage)?; 18 | 19 | if config.paused == true { 20 | return Err(ContractError::Std(StdError::generic_err( 21 | "The contract is temporarily paused", 22 | ))); 23 | } 24 | 25 | let lsside_contract_addr = if let Some(se) = config.ls_side_token { 26 | se 27 | } else { 28 | return Err(ContractError::Std(StdError::generic_err( 29 | "the lsSIDE token contract must have been registered", 30 | ))); 31 | }; 32 | 33 | match from_binary(&_cw20_msg.msg)? { 34 | Cw20HookMsg::Unbond {} => { 35 | if info.sender == lsside_contract_addr { 36 | try_withdraw(deps, env, info, _cw20_msg) 37 | } else { 38 | Err(ContractError::Std(StdError::generic_err("unauthorized"))) 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/staking.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | use std::u128; 3 | 4 | use cosmwasm_std::{Addr, Coin, CosmosMsg, StakingMsg, StdError, StdResult, Storage, Uint128}; 5 | use cosmwasm_std::{CustomQuery, DistributionMsg, QuerierWrapper}; 6 | use rust_decimal::prelude::*; 7 | use rust_decimal::Decimal; 8 | 9 | use crate::deposit::calc_fee; 10 | use crate::state::{LSSIDE_FROZEN_TOKENS, LSSIDE_FROZEN_TOTAL_ONCHAIN, STATE}; 11 | use crate::tokens::query_total_supply; 12 | use crate::types::config::CONFIG; 13 | use crate::types::killswitch::KillSwitch; 14 | use crate::types::validator_set::VALIDATOR_SET; 15 | 16 | pub fn lsside_exchange_rate( 17 | store: &dyn Storage, 18 | querier: QuerierWrapper, 19 | ) -> StdResult { 20 | let config = CONFIG.load(store)?; 21 | let state = STATE.load(store)?; 22 | let contract_address = config.contract_addr; 23 | 24 | let lsside_token = config 25 | .ls_side_token 26 | .ok_or_else(|| StdError::generic_err("lsSIDE token addr not registered".to_string()))?; 27 | 28 | if KillSwitch::try_from(config.kill_switch)? == KillSwitch::Closed { 29 | let total_on_chain = get_onchain_balance_with_rewards(querier, store, &contract_address)?; 30 | let tokens = query_total_supply(querier, &lsside_token)? 31 | .u128() 32 | .saturating_sub(state.lsside_to_burn.u128()); 33 | let exchange_rate = _calc_exchange_rate(total_on_chain, tokens)?; 34 | Ok(exchange_rate) 35 | } else { 36 | let total_on_chain = LSSIDE_FROZEN_TOTAL_ONCHAIN.load(store)?.u128(); 37 | let tokens = LSSIDE_FROZEN_TOKENS.load(store)?.u128(); 38 | 39 | let exchange_rate = _calc_exchange_rate(total_on_chain, tokens)?; 40 | Ok(exchange_rate) 41 | } 42 | } 43 | 44 | pub fn _calc_exchange_rate(total_on_chain: u128, tokens: u128) -> StdResult { 45 | let side_balance = Decimal::from(total_on_chain as u64); 46 | let token_bal = Decimal::from(tokens as u64); 47 | 48 | let ratio = if total_on_chain == 0 || tokens == 0 { 49 | Decimal::one() 50 | } else { 51 | side_balance.checked_div(token_bal).unwrap() 52 | }; 53 | 54 | Ok(ratio.round_dp_with_strategy(12, RoundingStrategy::AwayFromZero)) 55 | } 56 | 57 | // return net_onchain balance with rewards lsside using backing to calculate amount 58 | // check if we can also update backing here if total_staked differ with backing data 59 | pub fn get_onchain_balance_with_rewards( 60 | querier: QuerierWrapper, 61 | storage: &dyn Storage, 62 | contract_address: &Addr, 63 | ) -> StdResult { 64 | let config = CONFIG.load(storage)?; 65 | let state = STATE.load(storage)?; 66 | 67 | let rewards_balance = get_rewards(storage, querier, contract_address).unwrap_or_default(); 68 | 69 | let fee = calc_fee(rewards_balance, config.dev_fee); 70 | let final_rewards = Uint128::from(rewards_balance.u128().saturating_sub(fee as u128)); 71 | 72 | let lsside_onchain_balance = state.lsside_backing + Uint128::from(final_rewards.u128()); 73 | Ok(lsside_onchain_balance.u128()) 74 | } 75 | 76 | pub fn get_total_onchain_balance(storage: &dyn Storage) -> StdResult { 77 | let validator_set = VALIDATOR_SET.load(storage)?; 78 | let locked_balance = validator_set.total_staked(); 79 | 80 | let state = STATE.load(storage)?; 81 | let to_deposit_balance = state.to_deposit.u128(); 82 | 83 | Ok(locked_balance + to_deposit_balance) 84 | } 85 | 86 | pub fn get_balance( 87 | querier: QuerierWrapper, 88 | address: &Addr, 89 | ) -> StdResult { 90 | let balance = querier.query_balance(address.clone(), &"uside".to_string())?; 91 | 92 | Ok(balance.amount) 93 | } 94 | 95 | pub fn get_rewards( 96 | storage: &dyn Storage, 97 | querier: QuerierWrapper, 98 | contract: &Addr, 99 | ) -> StdResult { 100 | let validator_set = VALIDATOR_SET.load(storage)?; 101 | Ok(Uint128::from( 102 | validator_set.query_rewards(querier, contract.to_string())?, 103 | )) 104 | } 105 | 106 | pub fn stake_msg(validator: &str, amount: u128) -> CosmosMsg { 107 | CosmosMsg::Staking(StakingMsg::Delegate { 108 | validator: validator.to_string(), 109 | amount: Coin { 110 | denom: "uside".to_string(), 111 | amount: Uint128::from(amount), 112 | }, 113 | }) 114 | } 115 | 116 | pub fn undelegate_msg(validator: &str, amount: u128) -> CosmosMsg { 117 | CosmosMsg::Staking(StakingMsg::Undelegate { 118 | validator: validator.to_string(), 119 | amount: Coin { 120 | denom: "uside".to_string(), 121 | amount: Uint128::from(amount), 122 | }, 123 | }) 124 | } 125 | 126 | pub fn withdraw_msg(validator: &str) -> CosmosMsg { 127 | CosmosMsg::Distribution(DistributionMsg::WithdrawDelegatorReward { 128 | validator: validator.to_string(), 129 | }) 130 | } 131 | 132 | pub fn redelegate_msg(from: &str, to: &str, amount: u128) -> CosmosMsg { 133 | CosmosMsg::Staking(StakingMsg::Redelegate { 134 | src_validator: from.to_string(), 135 | amount: Coin { 136 | denom: "uside".to_string(), 137 | amount: Uint128::from(amount), 138 | }, 139 | dst_validator: to.to_string(), 140 | }) 141 | } 142 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/state.rs: -------------------------------------------------------------------------------- 1 | use cw_storage_plus::Item; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use cosmwasm_std::Uint128; 6 | 7 | pub const STATE: Item = Item::new("state"); 8 | 9 | pub const LSSIDE_FROZEN_TOTAL_ONCHAIN: Item = Item::new("lsside_frozen_total_onchain"); 10 | pub const LSSIDE_FROZEN_TOKENS: Item = Item::new("lsside_frozen_tokens"); 11 | 12 | #[derive(Serialize, Debug, Deserialize, Clone, PartialEq)] 13 | pub struct State { 14 | pub lsside_backing: Uint128, // amount of SIDE backing lsSIDE in circulation 15 | pub to_deposit: Uint128, // amount of SIDE to be deposited but not yet deposited to validators 16 | pub not_redeemed: Uint128, // amount of SIDE matured but not redeemed by user 17 | pub lsside_under_withdraw: Uint128, // amount of lsSIDE under 21 days withdraw 18 | pub side_under_withdraw: Uint128, 19 | pub lsside_to_burn: Uint128, 20 | } 21 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/tokens.rs: -------------------------------------------------------------------------------- 1 | use cw20::{Cw20Coin, Cw20QueryMsg, Logo, MinterResponse, TokenInfoResponse}; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use cosmwasm_std::{ 6 | to_binary, Addr, CustomQuery, QuerierWrapper, StdError, StdResult, Uint128, WasmQuery, 7 | }; 8 | 9 | pub fn query_total_supply( 10 | querier: QuerierWrapper, 11 | token_contract: &Addr, 12 | ) -> StdResult { 13 | let token_info_query = WasmQuery::Smart { 14 | contract_addr: token_contract.to_string(), 15 | msg: to_binary(&Cw20QueryMsg::TokenInfo {})?, 16 | }; 17 | 18 | let token_info = 19 | querier 20 | .query(&token_info_query.into()) 21 | .unwrap_or_else(|_| TokenInfoResponse { 22 | name: "NA".to_string(), 23 | symbol: "NA".to_string(), 24 | decimals: 6, 25 | total_supply: Uint128::from(0u128), 26 | }); 27 | 28 | Ok(token_info.total_supply) 29 | } 30 | 31 | #[derive(Serialize, Debug, Deserialize, JsonSchema, Clone, PartialEq)] 32 | pub struct Contract { 33 | pub address: Addr, 34 | pub hash: String, 35 | } 36 | 37 | #[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)] 38 | pub struct InstantiateMarketingInfo { 39 | pub project: Option, 40 | pub description: Option, 41 | pub marketing: Option, 42 | pub logo: Option, 43 | } 44 | 45 | /// TokenContract InitMsg 46 | #[derive(Serialize, Deserialize, JsonSchema)] 47 | pub struct TokenInitMsg { 48 | pub name: String, 49 | pub symbol: String, 50 | pub decimals: u8, 51 | pub initial_balances: Vec, 52 | pub mint: Option, 53 | pub marketing: Option, 54 | } 55 | 56 | #[allow(clippy::too_many_arguments)] 57 | impl TokenInitMsg { 58 | pub fn get_cap(&self) -> Option { 59 | self.mint.as_ref().and_then(|v| v.cap) 60 | } 61 | 62 | pub fn validate(&self) -> StdResult<()> { 63 | // Check name, symbol, decimals 64 | if !is_valid_name(&self.name) { 65 | return Err(StdError::generic_err( 66 | "Name is not in the expected format (3-50 UTF-8 bytes)", 67 | )); 68 | } 69 | if !is_valid_symbol(&self.symbol) { 70 | return Err(StdError::generic_err( 71 | "Ticker symbol is not in expected format [a-zA-Z\\-]{3,12}", 72 | )); 73 | } 74 | if self.decimals > 18 { 75 | return Err(StdError::generic_err("Decimals must not exceed 18")); 76 | } 77 | Ok(()) 78 | } 79 | } 80 | 81 | fn is_valid_name(name: &str) -> bool { 82 | let bytes = name.as_bytes(); 83 | if bytes.len() < 3 || bytes.len() > 50 { 84 | return false; 85 | } 86 | true 87 | } 88 | 89 | fn is_valid_symbol(symbol: &str) -> bool { 90 | let bytes = symbol.as_bytes(); 91 | if bytes.len() < 3 || bytes.len() > 12 { 92 | return false; 93 | } 94 | for byte in bytes.iter() { 95 | if (*byte != 45) && (*byte < 65 || *byte > 90) && (*byte < 97 || *byte > 122) { 96 | return false; 97 | } 98 | } 99 | true 100 | } 101 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/types/config.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::Addr; 2 | use cw_storage_plus::Item; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | pub const CONFIG: Item = Item::new("config"); 6 | 7 | #[derive(Serialize, Debug, Deserialize, Clone, PartialEq)] 8 | pub struct Config { 9 | pub admin: Addr, 10 | pub contract_addr: Addr, 11 | pub ls_side_token: Option, 12 | pub kill_switch: u8, 13 | pub epoch_period: u64, 14 | pub unbonding_period: u64, 15 | pub underlying_coin_denom: String, 16 | pub reward_denom: String, 17 | pub dev_address: Addr, 18 | pub dev_fee: u64, // 10^-3 percent. 1 = 0.001% 19 | pub referral_contract: Option, 20 | pub paused: bool, 21 | } 22 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/types/killswitch.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use cosmwasm_std::StdError; 4 | use std::convert::TryFrom; 5 | 6 | #[derive(Serialize, Debug, Deserialize, Clone, PartialEq)] 7 | pub enum KillSwitch { 8 | Closed, 9 | Unbonding, 10 | Open, 11 | } 12 | 13 | impl TryFrom for KillSwitch { 14 | type Error = StdError; 15 | 16 | fn try_from(other: u8) -> Result { 17 | match other { 18 | 0 => Ok(Self::Closed), 19 | 1 => Ok(Self::Unbonding), 20 | 2 => Ok(Self::Open), 21 | _ => Err(StdError::generic_err("Failed to convert killswitch enum")), 22 | } 23 | } 24 | } 25 | 26 | impl Into for KillSwitch { 27 | fn into(self) -> u8 { 28 | match self { 29 | Self::Closed => 0u8, 30 | Self::Unbonding => 1u8, 31 | Self::Open => 2u8, 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod config; 2 | pub(crate) mod killswitch; 3 | pub(crate) mod validator_set; 4 | pub(crate) mod window_manager; 5 | pub(crate) mod withdraw_window; 6 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/types/window_manager.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Addr, Order, StdError, StdResult, Storage, Uint128}; 2 | 3 | use cw_storage_plus::Item; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use rust_decimal::Decimal; 7 | use std::collections::VecDeque; 8 | 9 | use crate::msg::PendingClaimsResponse; 10 | use crate::types::withdraw_window::{ 11 | OngoingWithdrawWindow, QueueWindow, ONGOING_WITHDRAWS_AMOUNT, QUEUE_WINDOW_AMOUNT, 12 | }; 13 | use crate::utils::calc_withdraw; 14 | 15 | use crate::types::config::CONFIG; 16 | 17 | pub const WINDOW_MANANGER: Item = Item::new("window_manager"); 18 | 19 | #[derive(Serialize, Deserialize, Clone, Debug, Default)] 20 | pub struct WindowManager { 21 | pub time_to_close_window: u64, 22 | pub queue_window: QueueWindow, 23 | pub ongoing_windows: VecDeque, 24 | } 25 | 26 | impl WindowManager { 27 | pub fn add_user_amount_to_active_window( 28 | &mut self, 29 | store: &mut dyn Storage, 30 | user_addr: Addr, 31 | lsside_amount: Uint128, 32 | ) -> StdResult<()> { 33 | if let Some(mut already_stored_amount) = QUEUE_WINDOW_AMOUNT.may_load(store, &user_addr)? { 34 | already_stored_amount += lsside_amount; 35 | QUEUE_WINDOW_AMOUNT.save(store, &user_addr, &already_stored_amount)?; 36 | } else { 37 | QUEUE_WINDOW_AMOUNT.save(store, &user_addr, &lsside_amount)?; 38 | } 39 | 40 | self.queue_window.total_lsside += lsside_amount; 41 | 42 | Ok(()) 43 | } 44 | 45 | pub fn get_user_lsside_in_active_window( 46 | &self, 47 | store: &dyn Storage, 48 | user_addr: Addr, 49 | ) -> StdResult { 50 | let mut lsside_amount = Uint128::from(0u128); 51 | if let Some(lsside_amount_got) = QUEUE_WINDOW_AMOUNT.may_load(store, &user_addr)? { 52 | lsside_amount = lsside_amount_got; 53 | } 54 | 55 | Ok(lsside_amount) 56 | } 57 | 58 | pub fn advance_window( 59 | &mut self, 60 | store: &mut dyn Storage, 61 | current_time: u64, 62 | exchange_rate_lsside: Decimal, 63 | ) -> StdResult<()> { 64 | let config = CONFIG.load(store)?; 65 | let queue_window = self.queue_window.clone(); 66 | let queue_amounts: StdResult> = QUEUE_WINDOW_AMOUNT 67 | .range(store, None, None, Order::Ascending) 68 | .collect(); 69 | 70 | let lsside_to_side = Uint128::from(calc_withdraw( 71 | queue_window.total_lsside, 72 | exchange_rate_lsside, 73 | )?); 74 | 75 | self.ongoing_windows.push_back(OngoingWithdrawWindow { 76 | id: queue_window.id, 77 | time_to_mature_window: current_time + config.unbonding_period, 78 | total_side: lsside_to_side, 79 | total_lsside: queue_window.total_lsside, 80 | }); 81 | for (user_addr, queue_amt) in queue_amounts?.iter() { 82 | ONGOING_WITHDRAWS_AMOUNT.save( 83 | store, 84 | (&queue_window.id.to_string(), user_addr), 85 | &Uint128::from(calc_withdraw(*queue_amt, exchange_rate_lsside).unwrap()), 86 | )?; 87 | 88 | QUEUE_WINDOW_AMOUNT.remove(store, user_addr); 89 | } 90 | 91 | self.time_to_close_window = current_time + config.epoch_period; 92 | self.queue_window = QueueWindow { 93 | id: queue_window.id + 1, 94 | total_lsside: Uint128::from(0u128), 95 | }; 96 | 97 | Ok(()) 98 | } 99 | 100 | pub fn pop_matured(&mut self, _store: &dyn Storage) -> StdResult { 101 | if let Some(matured_window) = self.ongoing_windows.pop_front() { 102 | Ok(matured_window) 103 | } else { 104 | return Err(StdError::generic_err("Previous windows deque empty")); 105 | } 106 | } 107 | 108 | pub fn get_user_pending_withdraws( 109 | &self, 110 | store: &dyn Storage, 111 | address: Addr, 112 | ) -> StdResult> { 113 | let mut pending_withdraws: Vec = vec![]; 114 | 115 | for ongoing_window in self.ongoing_windows.iter() { 116 | let window_id = ongoing_window.id.to_string(); 117 | 118 | if let Some(user_withdraw_amount) = 119 | ONGOING_WITHDRAWS_AMOUNT.may_load(store, (&window_id.to_string(), &address))? 120 | { 121 | if user_withdraw_amount > Uint128::from(0u128) { 122 | pending_withdraws.push(PendingClaimsResponse { 123 | window_id: ongoing_window.id, 124 | claim_time: ongoing_window.time_to_mature_window.clone(), 125 | side_amount: user_withdraw_amount, 126 | }) 127 | } 128 | } 129 | } 130 | Ok(pending_withdraws) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/types/withdraw_window.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Addr, Uint128}; 2 | use cw_storage_plus::{Item, Map}; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | pub const USER_CLAIMABLE: Item = Item::new("user_claimable"); 6 | 7 | pub const QUEUE_WINDOW_AMOUNT: Map<&Addr, Uint128> = Map::new("queue_window"); 8 | pub const ONGOING_WITHDRAWS_AMOUNT: Map<(&str, &Addr), Uint128> = Map::new("ongoing_withdraws"); 9 | pub const USER_CLAIMABLE_AMOUNT: Map<&Addr, Uint128> = Map::new("user_claimable_map"); 10 | 11 | #[derive(Serialize, Deserialize, Clone, Debug, Default)] 12 | pub struct QueueWindow { 13 | pub id: u64, 14 | pub total_lsside: Uint128, 15 | } 16 | 17 | #[derive(Serialize, Deserialize, Clone, Debug, Default)] 18 | pub struct OngoingWithdrawWindow { 19 | pub id: u64, 20 | pub time_to_mature_window: u64, 21 | pub total_side: Uint128, 22 | pub total_lsside: Uint128, 23 | } 24 | 25 | #[derive(Serialize, Deserialize, Clone, Debug, Default)] 26 | pub struct UserClaimable { 27 | pub total_side: Uint128, 28 | } 29 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/update.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, StdError, Uint128}; 2 | 3 | use crate::query::query_delegation; 4 | use crate::types::config::CONFIG; 5 | use crate::types::validator_set::VALIDATOR_SET; 6 | use crate::ContractError; 7 | 8 | /** 9 | * Update lsside token addr in config 10 | */ 11 | pub fn try_update_lsside_addr( 12 | deps: DepsMut, 13 | _env: Env, 14 | info: MessageInfo, 15 | address: Addr, 16 | ) -> Result { 17 | let mut config = CONFIG.load(deps.storage)?; 18 | 19 | if info.sender != config.admin { 20 | return Err(ContractError::Std(StdError::generic_err( 21 | "Only admin can update lsside Address", 22 | ))); 23 | } 24 | 25 | config.ls_side_token = Some(address); 26 | CONFIG.save(deps.storage, &config)?; 27 | 28 | Ok(Response::new()) 29 | } 30 | 31 | /** 32 | * Rebalance staked amount according to current onchain delegation 33 | */ 34 | pub fn rebalance_slash(deps: DepsMut, env: Env) -> Result { 35 | let mut validator_set = VALIDATOR_SET.load(deps.storage)?; 36 | for val in validator_set.validators.iter_mut() { 37 | let current_amount = query_delegation(&deps.querier, &val.address, &env.contract.address)?; 38 | // what if zero ? 39 | if current_amount > 0 { 40 | val.staked = Uint128::from(current_amount); 41 | } 42 | } 43 | 44 | validator_set.rebalance(); 45 | VALIDATOR_SET.save(deps.storage, &validator_set)?; 46 | Ok(Response::new()) 47 | } 48 | -------------------------------------------------------------------------------- /contracts/staking_contract/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use cosmwasm_std::{StdError, StdResult, Uint128}; 4 | use rust_decimal::prelude::ToPrimitive; 5 | use rust_decimal::Decimal; 6 | 7 | /** Inflation rate, and other fun things are in the form 0.xxxxx. 8 | * To use we remove the leading '0.' 9 | * and cut all but the the first 4 digits 10 | */ 11 | #[allow(dead_code)] 12 | pub fn dec_to_uint(dec: String) -> StdResult { 13 | let tokens: Vec<&str> = dec.split('.').collect(); 14 | 15 | if tokens.len() < 2 { 16 | return u128::from_str(&dec).map_err(|_| StdError::generic_err("failed to parse number")); 17 | } 18 | 19 | u128::from_str(&dec).map_err(|_| StdError::generic_err("failed to parse number")) 20 | } 21 | 22 | /** 23 | * Calculates how much your withdrawn tokens are worth in SIDE 24 | */ 25 | pub fn calc_withdraw(amount: Uint128, exchange_rate: Decimal) -> StdResult { 26 | let normalized_amount = Decimal::from(amount.u128() as u64); 27 | 28 | let raw_amount = normalized_amount 29 | .checked_mul(exchange_rate) 30 | .unwrap_or(Decimal::from(0u128)); 31 | 32 | let coins_to_withdraw = raw_amount.to_u128().unwrap(); 33 | 34 | Ok(coins_to_withdraw) 35 | } 36 | 37 | /** 38 | * Calculates threshold amount from reward amount 39 | * and count of validators. 40 | * 41 | * Returns amount of SIDE tokens as threshold value. 42 | */ 43 | pub fn calc_threshold(amount: u128, val_count: usize) -> u128 { 44 | amount.checked_div(val_count as u128).unwrap_or(0) 45 | } 46 | -------------------------------------------------------------------------------- /contracts/volume/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideprotocol/mesh-liquidity-wasm/6a7f5f1f7cd2344311f6bfe17ac0be41bfe782a2/contracts/volume/.DS_Store -------------------------------------------------------------------------------- /contracts/volume/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "volume" 3 | version = "0.2.0" 4 | edition = "2021" 5 | description = "Track volume" 6 | license = "Apache-2.0" 7 | 8 | [lib] 9 | crate-type = ["cdylib", "rlib"] 10 | 11 | [features] 12 | backtraces = ["cosmwasm-std/backtraces"] 13 | # use library feature to disable all instantiate/execute/query exports 14 | library = [] 15 | 16 | [dependencies] 17 | cw-utils = { version = "1.0.1" } 18 | cw2 = { version = "1.0.1" } 19 | cw20 = { version = "1.0.1" } 20 | cosmwasm-std = { version = "1.0.0-beta3", features = ["stargate"] } 21 | cw-storage-plus = { version = "1.0.1" } 22 | schemars = "0.8.1" 23 | serde = { version = "1.0.103", default-features = false, features = ["derive"] } 24 | thiserror = { version = "1.0.23" } 25 | hex = "0.3.1" 26 | sha2 = "0.8.0" 27 | 28 | [dev-dependencies] 29 | cosmwasm-schema = { version = "1.0.0-beta3" } -------------------------------------------------------------------------------- /contracts/volume/examples/schema.rs: -------------------------------------------------------------------------------- 1 | use std::env::current_dir; 2 | use std::fs::create_dir_all; 3 | 4 | use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; 5 | 6 | use volume::msg::ExecuteMsg; 7 | use volume::msg::InstantiateMsg; 8 | use volume::msg::QueryMsg; 9 | 10 | fn main() { 11 | let mut out_dir = current_dir().unwrap(); 12 | out_dir.push("schema"); 13 | create_dir_all(&out_dir).unwrap(); 14 | remove_schemas(&out_dir).unwrap(); 15 | 16 | export_schema(&schema_for!(InstantiateMsg), &out_dir); 17 | export_schema(&schema_for!(ExecuteMsg), &out_dir); 18 | export_schema(&schema_for!(QueryMsg), &out_dir); 19 | } 20 | -------------------------------------------------------------------------------- /contracts/volume/schema/execute_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "ExecuteMsg", 4 | "oneOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "LogObservation" 9 | ], 10 | "properties": { 11 | "LogObservation": { 12 | "type": "object", 13 | "required": [ 14 | "token1", 15 | "token2" 16 | ], 17 | "properties": { 18 | "token1": { 19 | "$ref": "#/definitions/Coin" 20 | }, 21 | "token2": { 22 | "$ref": "#/definitions/Coin" 23 | } 24 | } 25 | } 26 | }, 27 | "additionalProperties": false 28 | }, 29 | { 30 | "type": "object", 31 | "required": [ 32 | "SetContract" 33 | ], 34 | "properties": { 35 | "SetContract": { 36 | "type": "object", 37 | "required": [ 38 | "address" 39 | ], 40 | "properties": { 41 | "address": { 42 | "type": "string" 43 | } 44 | } 45 | } 46 | }, 47 | "additionalProperties": false 48 | } 49 | ], 50 | "definitions": { 51 | "Coin": { 52 | "type": "object", 53 | "required": [ 54 | "amount", 55 | "denom" 56 | ], 57 | "properties": { 58 | "amount": { 59 | "$ref": "#/definitions/Uint128" 60 | }, 61 | "denom": { 62 | "type": "string" 63 | } 64 | } 65 | }, 66 | "Uint128": { 67 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 68 | "type": "string" 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /contracts/volume/schema/instantiate_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "type": "object", 5 | "required": [ 6 | "contract", 7 | "max_length" 8 | ], 9 | "properties": { 10 | "contract": { 11 | "type": "string" 12 | }, 13 | "max_length": { 14 | "type": "integer", 15 | "format": "uint64", 16 | "minimum": 0.0 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/volume/schema/query_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "QueryMsg", 4 | "oneOf": [ 5 | { 6 | "description": "Returns volume between specific interval Returns total volume till latest timestamp", 7 | "type": "object", 8 | "required": [ 9 | "total_volume" 10 | ], 11 | "properties": { 12 | "total_volume": { 13 | "type": "object" 14 | } 15 | }, 16 | "additionalProperties": false 17 | }, 18 | { 19 | "description": "Returns total volume till given timestamp", 20 | "type": "object", 21 | "required": [ 22 | "total_volume_at" 23 | ], 24 | "properties": { 25 | "total_volume_at": { 26 | "type": "object", 27 | "required": [ 28 | "timestamp" 29 | ], 30 | "properties": { 31 | "timestamp": { 32 | "type": "integer", 33 | "format": "uint64", 34 | "minimum": 0.0 35 | } 36 | } 37 | } 38 | }, 39 | "additionalProperties": false 40 | }, 41 | { 42 | "type": "object", 43 | "required": [ 44 | "volume24" 45 | ], 46 | "properties": { 47 | "volume24": { 48 | "type": "object" 49 | } 50 | }, 51 | "additionalProperties": false 52 | }, 53 | { 54 | "description": "Returns contract address for which volume is tracked", 55 | "type": "object", 56 | "required": [ 57 | "contract" 58 | ], 59 | "properties": { 60 | "contract": { 61 | "type": "object" 62 | } 63 | }, 64 | "additionalProperties": false 65 | } 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /contracts/volume/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum Never {} 6 | 7 | #[derive(Error, Debug, PartialEq)] 8 | pub enum ContractError { 9 | #[error("{0}")] 10 | Std(#[from] StdError), 11 | 12 | #[error("Send some coins to create an atomic swap")] 13 | EmptyBalance {}, 14 | 15 | #[error("Atomic swap not yet expired")] 16 | NotExpired, 17 | 18 | #[error("Expired atomic swap")] 19 | Expired, 20 | 21 | #[error("Atomic swap already exists")] 22 | AlreadyExists, 23 | 24 | #[error("Order already taken")] 25 | OrderTaken, 26 | 27 | #[error("Order is not for this chain")] 28 | InvalidChain, 29 | 30 | #[error("Invalid sell token")] 31 | InvalidSellToken, 32 | 33 | #[error("Order has already been taken")] 34 | AlreadyTakenOrder, 35 | 36 | #[error("Invalid taker address")] 37 | InvalidTakerAddress, 38 | 39 | #[error("Invalid maker address")] 40 | InvalidMakerAddress, 41 | 42 | #[error("Invalid sender address")] 43 | InvalidSender, 44 | 45 | #[error("Invalid status")] 46 | InvalidStatus, 47 | 48 | #[error("Got a submessage reply with unknown id: {id}")] 49 | UnknownReplyId { id: u64 }, 50 | 51 | #[error("Only supports channel with ibc version ics100-1, got {version}")] 52 | InvalidIbcVersion { version: String }, 53 | 54 | #[error("Only supports unordered channel")] 55 | OnlyOrderedChannel {}, 56 | 57 | #[error("Only accepts tokens that originate on this chain, not native tokens of remote chain")] 58 | NoForeignTokens {}, 59 | 60 | #[error("Parsed port from denom ({port}) doesn't match packet")] 61 | FromOtherPort { port: String }, 62 | 63 | #[error("Parsed channel from denom ({channel}) doesn't match packet")] 64 | FromOtherChannel { channel: String }, 65 | 66 | #[error("Bid is not allowed for this order")] 67 | TakeBidNotAllowed, 68 | 69 | #[error("Bid already exist")] 70 | BidAlreadyExist, 71 | 72 | #[error("Bid doesn't exist")] 73 | BidDoesntExist, 74 | } 75 | -------------------------------------------------------------------------------- /contracts/volume/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod error; 3 | pub mod msg; 4 | pub mod state; 5 | 6 | pub use crate::error::ContractError; 7 | -------------------------------------------------------------------------------- /contracts/volume/src/msg.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use cosmwasm_std::Coin; 5 | 6 | #[derive(Serialize, Deserialize, JsonSchema)] 7 | pub struct InstantiateMsg { 8 | pub contract: String, 9 | pub max_length: u64, 10 | } 11 | 12 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 13 | pub enum ExecuteMsg { 14 | LogObservation { token1: Coin, token2: Coin }, 15 | SetContract { address: String }, 16 | } 17 | 18 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 19 | pub struct MigrateMsg {} 20 | 21 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 22 | #[serde(rename_all = "snake_case")] 23 | pub enum QueryMsg { 24 | /// Returns volume between specific interval 25 | // VolumeInterval { 26 | // start: u64, 27 | // end: u64, 28 | // }, 29 | /// Returns total volume till latest timestamp 30 | TotalVolume {}, 31 | /// Returns total volume till given timestamp 32 | TotalVolumeAt { timestamp: u64 }, 33 | Volume24 {}, 34 | /// Returns contract address for which volume is tracked 35 | Contract {}, 36 | } 37 | 38 | // #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 39 | // pub struct DetailsResponse { 40 | // pub id: String, 41 | // pub maker: MakeSwapMsg, 42 | // pub status: Status, 43 | // pub path: String, 44 | // pub taker: Option, 45 | // pub cancel_timestamp: Option, 46 | // pub complete_timestamp: Option, 47 | // } 48 | // #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 49 | // pub struct ListResponse { 50 | // /// List all open swap ids 51 | // pub swaps: Vec, 52 | // } 53 | -------------------------------------------------------------------------------- /contracts/volume/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::Coin; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use cw_storage_plus::{Item, Map}; 6 | 7 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 8 | pub struct Observation { 9 | // timestamp 10 | pub block_timestamp: u64, 11 | // Number of observations till block_timestamp 12 | pub num_of_observations: u64, 13 | // volume cumulative token1 14 | pub volume1: u128, 15 | // volume cumulative token2 16 | pub volume2: u128, 17 | } 18 | 19 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 20 | pub struct ObservationOutput { 21 | // timestamp 22 | pub block_timestamp: u64, 23 | // Number of observations till block_timestamp 24 | pub num_of_observations: u64, 25 | // volume cumulative token1 26 | pub volume1: Coin, 27 | // volume cumulative token2 28 | pub volume2: Coin, 29 | } 30 | 31 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 32 | pub struct Config { 33 | // admin 34 | pub admin: String, 35 | // can only be called by contract 36 | pub contract_address: String, 37 | // current index 38 | pub current_idx: u64, 39 | // pivoted or not 40 | pub pivoted: bool, 41 | // Maximum length 42 | pub max_length: u64, 43 | // Is new 44 | pub is_new: bool, 45 | // total observations in map 46 | pub counter: u64, 47 | } 48 | 49 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 50 | pub struct Order { 51 | pub token1: String, 52 | pub token2: String, 53 | } 54 | 55 | pub const OBSERVATIONS: Map = Map::new("observations"); 56 | 57 | pub const CONFIG: Item = Item::new("config"); 58 | 59 | pub const ORDER: Item = Item::new("order"); 60 | 61 | -------------------------------------------------------------------------------- /contracts/voting-escrow/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ve-side" 3 | version = "1.0.0" 4 | authors = ["Amit Yadav"] 5 | edition = "2021" 6 | 7 | exclude = [ 8 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 9 | "contract.wasm", 10 | "hash.txt", 11 | ] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | crate-type = ["cdylib", "rlib"] 17 | 18 | [features] 19 | # for quicker tests, cargo test --lib 20 | # for more explicit tests, cargo test --features=backtraces 21 | backtraces = ["cosmwasm-std/backtraces"] 22 | 23 | [dependencies] 24 | cw2 = "0.15" 25 | cw20 = "0.15" 26 | cw20-base = { version = "0.15", features = ["library"] } 27 | cosmwasm-std = "1.1" 28 | cw-storage-plus = "0.15" 29 | thiserror = { version = "1.0" } 30 | cosmwasm-schema = "1.1" 31 | 32 | [dev-dependencies] 33 | cw-multi-test = "0.15" 34 | anyhow = "1" 35 | proptest = "1.0" 36 | -------------------------------------------------------------------------------- /contracts/voting-escrow/README.md: -------------------------------------------------------------------------------- 1 | # Vote-Escrowed SIDE (veSIDE) 2 | 3 | # Features 4 | 5 | - veSIDE is represented by the VotingEscrow contract. 6 | - veSIDE cannot be transferred. The only way to obtain veSIDE is by locking SIDE-ATOM LP. The maximum lock time is not set yet. 7 | - A user’s veSIDE balance decays linearly as the remaining time until the LP unlock decreases. For example, a balance of 4000 LP locked for one year provides the same amount of veSIDE as 2000 LP locked for two years, or 1000 LP locked for four years. 8 | 9 | # Implementation Details 10 | 11 | User voting power `w_i` is linearly decreasing since the moment of lock. So does the total voting power `W`. In order to avoid periodic check-ins, every time the user deposits, or withdraws, or changes the locktime, we record user’s slope and bias for the linear function `w_i_t` in the public mapping `user_point_history`. We also change slope and bias for the total voting power `W_t` and record it in `point_history`. In addition, when a user’s lock is scheduled to end, we schedule change of slopes of `W_t`in the future in `slope_changes`. Every change involves increasing the `epoch` by 1. 12 | 13 | This way we don’t have to iterate over all users to figure out, how much should 14 | `W_t`change by, neither we require users to check in periodically. However, we limit the end of user locks to times rounded off by whole weeks. 15 | 16 | Slopes and biases change both when a user deposits and locks governance tokens, and when the locktime expires. All the possible expiration times are rounded to whole weeks to make number of reads from blockchain proportional to number of missed weeks at most, not number of users (which is potentially large). 17 | 18 | Ref: [CRV](https://curve.readthedocs.io/dao-vecrv.html), [BAL](https://docs.balancer.fi/concepts/governance/veBAL/) 19 | 20 | # Querying Balances, Locks and supply 21 | 22 | ## User balance 23 | ``` 24 | /// Return the user's veSIDE balance 25 | Balance { address: String }, 26 | ``` 27 | `address`: User address 28 | 29 | ## TokenInfo 30 | ``` 31 | /// Fetch the veSIDE token information 32 | TokenInfo {}, 33 | ``` 34 | 35 | ## Total voting power 36 | ``` 37 | /// Return the current total amount of veSIDE 38 | TotalVotingPower {}, 39 | ``` 40 | 41 | ## Total voting power at some point 42 | ``` 43 | /// Return the total amount of veSIDE at some point in the past 44 | TotalVotingPowerAt { time: u64 }, 45 | ``` 46 | `time`: Time in seconds 47 | 48 | ## User voting power 49 | ``` 50 | /// Return the user's current voting power (veSIDE balance) 51 | UserVotingPower { user: String }, 52 | ``` 53 | `user`: User address 54 | 55 | ## User voting power at some point 56 | ``` 57 | /// Return the user's veSIDE balance at some point in the past 58 | UserVotingPowerAt { user: String, time: u64 }, 59 | ``` 60 | `user`: User address 61 | `time`: Time in seconds 62 | 63 | ## User lock info 64 | ``` 65 | /// Return information about a user's lock position 66 | LockInfo { user: String }, 67 | ``` 68 | `user`: User address 69 | 70 | ## User deposit at height 71 | ``` 72 | /// Return user's locked LP balance at the given block height 73 | UserDepositAtHeight { user: String, height: u64 }, 74 | ``` 75 | `user`: User address 76 | `height`: Block height 77 | -------------------------------------------------------------------------------- /contracts/voting-escrow/examples/schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | use ve_side::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; 3 | 4 | fn main() { 5 | write_api! { 6 | instantiate: InstantiateMsg, 7 | query: QueryMsg, 8 | execute: ExecuteMsg, 9 | migrate: MigrateMsg, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/execute.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "ExecuteMsg", 4 | "description": "This structure describes the execute functions in the contract.", 5 | "oneOf": [ 6 | { 7 | "description": "Extend the lockup time for your staked LP", 8 | "type": "object", 9 | "required": [ 10 | "extend_lock_time" 11 | ], 12 | "properties": { 13 | "extend_lock_time": { 14 | "type": "object", 15 | "required": [ 16 | "time" 17 | ], 18 | "properties": { 19 | "time": { 20 | "type": "integer", 21 | "format": "uint64", 22 | "minimum": 0.0 23 | } 24 | }, 25 | "additionalProperties": false 26 | } 27 | }, 28 | "additionalProperties": false 29 | }, 30 | { 31 | "description": "Receives a message of type [`Cw20ReceiveMsg`] and processes it depending on the received template.", 32 | "type": "object", 33 | "required": [ 34 | "receive" 35 | ], 36 | "properties": { 37 | "receive": { 38 | "$ref": "#/definitions/Cw20ReceiveMsg" 39 | } 40 | }, 41 | "additionalProperties": false 42 | }, 43 | { 44 | "description": "Withdraw LP from the contract", 45 | "type": "object", 46 | "required": [ 47 | "withdraw" 48 | ], 49 | "properties": { 50 | "withdraw": { 51 | "type": "object", 52 | "additionalProperties": false 53 | } 54 | }, 55 | "additionalProperties": false 56 | } 57 | ], 58 | "definitions": { 59 | "Binary": { 60 | "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", 61 | "type": "string" 62 | }, 63 | "Cw20ReceiveMsg": { 64 | "description": "Cw20ReceiveMsg should be de/serialized under `Receive()` variant in a ExecuteMsg", 65 | "type": "object", 66 | "required": [ 67 | "amount", 68 | "msg", 69 | "sender" 70 | ], 71 | "properties": { 72 | "amount": { 73 | "$ref": "#/definitions/Uint128" 74 | }, 75 | "msg": { 76 | "$ref": "#/definitions/Binary" 77 | }, 78 | "sender": { 79 | "type": "string" 80 | } 81 | }, 82 | "additionalProperties": false 83 | }, 84 | "Uint128": { 85 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 86 | "type": "string" 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/instantiate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "type": "object", 5 | "required": [ 6 | "admin", 7 | "deposit_token" 8 | ], 9 | "properties": { 10 | "admin": { 11 | "type": "string" 12 | }, 13 | "deposit_token": { 14 | "type": "string" 15 | }, 16 | "guardian_addr": { 17 | "type": [ 18 | "string", 19 | "null" 20 | ] 21 | } 22 | }, 23 | "additionalProperties": false 24 | } 25 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/migrate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "MigrateMsg", 4 | "description": "This structure describes a Migration message.", 5 | "type": "object", 6 | "additionalProperties": false 7 | } 8 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_balance.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "BalanceResponse", 4 | "type": "object", 5 | "required": [ 6 | "balance" 7 | ], 8 | "properties": { 9 | "balance": { 10 | "$ref": "#/definitions/Uint128" 11 | } 12 | }, 13 | "additionalProperties": false, 14 | "definitions": { 15 | "Uint128": { 16 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 17 | "type": "string" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "ConfigResponse", 4 | "description": "This structure stores the parameters returned when querying for a contract's configuration.", 5 | "type": "object", 6 | "required": [ 7 | "admin", 8 | "deposit_token" 9 | ], 10 | "properties": { 11 | "admin": { 12 | "type": "string" 13 | }, 14 | "deposit_token": { 15 | "type": "string" 16 | } 17 | }, 18 | "additionalProperties": false 19 | } 20 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_lock_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "LockInfoResponse", 4 | "description": "This structure is used to return the lock information for a veSIDE position.", 5 | "type": "object", 6 | "required": [ 7 | "amount", 8 | "coefficient", 9 | "end", 10 | "slope", 11 | "start" 12 | ], 13 | "properties": { 14 | "amount": { 15 | "description": "The amount of LP locked in the position", 16 | "allOf": [ 17 | { 18 | "$ref": "#/definitions/Uint128" 19 | } 20 | ] 21 | }, 22 | "coefficient": { 23 | "description": "This is the initial boost for the lock position", 24 | "allOf": [ 25 | { 26 | "$ref": "#/definitions/Decimal" 27 | } 28 | ] 29 | }, 30 | "end": { 31 | "description": "End time for the veSIDE position decay", 32 | "type": "integer", 33 | "format": "uint64", 34 | "minimum": 0.0 35 | }, 36 | "slope": { 37 | "description": "Slope at which a staker's veSIDE balance decreases over time", 38 | "allOf": [ 39 | { 40 | "$ref": "#/definitions/Uint128" 41 | } 42 | ] 43 | }, 44 | "start": { 45 | "description": "Start time for the veSIDE position decay", 46 | "type": "integer", 47 | "format": "uint64", 48 | "minimum": 0.0 49 | } 50 | }, 51 | "additionalProperties": false, 52 | "definitions": { 53 | "Decimal": { 54 | "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", 55 | "type": "string" 56 | }, 57 | "Uint128": { 58 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 59 | "type": "string" 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_simulate_lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Point", 4 | "description": "This structure stores points along the checkpoint history for every LP staker.", 5 | "type": "object", 6 | "required": [ 7 | "end", 8 | "power", 9 | "slope", 10 | "start" 11 | ], 12 | "properties": { 13 | "end": { 14 | "description": "The period when the lock should expire", 15 | "type": "integer", 16 | "format": "uint64", 17 | "minimum": 0.0 18 | }, 19 | "power": { 20 | "description": "The staker's veSIDE voting power", 21 | "allOf": [ 22 | { 23 | "$ref": "#/definitions/Uint128" 24 | } 25 | ] 26 | }, 27 | "slope": { 28 | "description": "Weekly voting power decay", 29 | "allOf": [ 30 | { 31 | "$ref": "#/definitions/Uint128" 32 | } 33 | ] 34 | }, 35 | "start": { 36 | "description": "The start period when the staker's voting power start to decrease", 37 | "type": "integer", 38 | "format": "uint64", 39 | "minimum": 0.0 40 | } 41 | }, 42 | "additionalProperties": false, 43 | "definitions": { 44 | "Uint128": { 45 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 46 | "type": "string" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_token_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "TokenInfoResponse", 4 | "type": "object", 5 | "required": [ 6 | "decimals", 7 | "name", 8 | "symbol", 9 | "total_supply" 10 | ], 11 | "properties": { 12 | "decimals": { 13 | "type": "integer", 14 | "format": "uint8", 15 | "minimum": 0.0 16 | }, 17 | "name": { 18 | "type": "string" 19 | }, 20 | "symbol": { 21 | "type": "string" 22 | }, 23 | "total_supply": { 24 | "$ref": "#/definitions/Uint128" 25 | } 26 | }, 27 | "additionalProperties": false, 28 | "definitions": { 29 | "Uint128": { 30 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 31 | "type": "string" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_total_voting_power.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "VotingPowerResponse", 4 | "description": "This structure is used to return a user's amount of veSIDE.", 5 | "type": "object", 6 | "required": [ 7 | "voting_power" 8 | ], 9 | "properties": { 10 | "voting_power": { 11 | "description": "The veSIDE balance", 12 | "allOf": [ 13 | { 14 | "$ref": "#/definitions/Uint128" 15 | } 16 | ] 17 | } 18 | }, 19 | "additionalProperties": false, 20 | "definitions": { 21 | "Uint128": { 22 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 23 | "type": "string" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_total_voting_power_at.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "VotingPowerResponse", 4 | "description": "This structure is used to return a user's amount of veSIDE.", 5 | "type": "object", 6 | "required": [ 7 | "voting_power" 8 | ], 9 | "properties": { 10 | "voting_power": { 11 | "description": "The veSIDE balance", 12 | "allOf": [ 13 | { 14 | "$ref": "#/definitions/Uint128" 15 | } 16 | ] 17 | } 18 | }, 19 | "additionalProperties": false, 20 | "definitions": { 21 | "Uint128": { 22 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 23 | "type": "string" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_total_voting_power_at_period.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "VotingPowerResponse", 4 | "description": "This structure is used to return a user's amount of veSIDE.", 5 | "type": "object", 6 | "required": [ 7 | "voting_power" 8 | ], 9 | "properties": { 10 | "voting_power": { 11 | "description": "The veSIDE balance", 12 | "allOf": [ 13 | { 14 | "$ref": "#/definitions/Uint128" 15 | } 16 | ] 17 | } 18 | }, 19 | "additionalProperties": false, 20 | "definitions": { 21 | "Uint128": { 22 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 23 | "type": "string" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_user_deposit_at_height.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Uint128", 4 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 5 | "type": "string" 6 | } 7 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_user_voting_power.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "VotingPowerResponse", 4 | "description": "This structure is used to return a user's amount of veSIDE.", 5 | "type": "object", 6 | "required": [ 7 | "voting_power" 8 | ], 9 | "properties": { 10 | "voting_power": { 11 | "description": "The veSIDE balance", 12 | "allOf": [ 13 | { 14 | "$ref": "#/definitions/Uint128" 15 | } 16 | ] 17 | } 18 | }, 19 | "additionalProperties": false, 20 | "definitions": { 21 | "Uint128": { 22 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 23 | "type": "string" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_user_voting_power_at.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "VotingPowerResponse", 4 | "description": "This structure is used to return a user's amount of veSIDE.", 5 | "type": "object", 6 | "required": [ 7 | "voting_power" 8 | ], 9 | "properties": { 10 | "voting_power": { 11 | "description": "The veSIDE balance", 12 | "allOf": [ 13 | { 14 | "$ref": "#/definitions/Uint128" 15 | } 16 | ] 17 | } 18 | }, 19 | "additionalProperties": false, 20 | "definitions": { 21 | "Uint128": { 22 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 23 | "type": "string" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/voting-escrow/schema/raw/response_to_user_voting_power_at_period.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "VotingPowerResponse", 4 | "description": "This structure is used to return a user's amount of veSIDE.", 5 | "type": "object", 6 | "required": [ 7 | "voting_power" 8 | ], 9 | "properties": { 10 | "voting_power": { 11 | "description": "The veSIDE balance", 12 | "allOf": [ 13 | { 14 | "$ref": "#/definitions/Uint128" 15 | } 16 | ] 17 | } 18 | }, 19 | "additionalProperties": false, 20 | "definitions": { 21 | "Uint128": { 22 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 23 | "type": "string" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/voting-escrow/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use cw20_base::ContractError as cw20baseError; 3 | use thiserror::Error; 4 | 5 | /// This enum describes vxASTRO contract errors 6 | #[derive(Error, Debug)] 7 | pub enum ContractError { 8 | #[error("{0}")] 9 | Std(#[from] StdError), 10 | 11 | #[error("{0}")] 12 | Cw20Base(#[from] cw20baseError), 13 | 14 | #[error("Unauthorized")] 15 | Unauthorized {}, 16 | 17 | #[error("Lock already exists")] 18 | LockAlreadyExists {}, 19 | 20 | #[error("Lock does not exist")] 21 | LockDoesNotExist {}, 22 | 23 | #[error("Lock time must be within limits (week <= lock time < 2 years)")] 24 | LockTimeLimitsError {}, 25 | 26 | #[error("The lock time has not yet expired")] 27 | LockHasNotExpired {}, 28 | 29 | #[error("The lock expired. Withdraw and create new lock")] 30 | LockExpired {}, 31 | 32 | #[error("Contract can't be migrated!")] 33 | MigrationError {}, 34 | } 35 | -------------------------------------------------------------------------------- /contracts/voting-escrow/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | pub mod msg; 3 | pub mod state; 4 | 5 | pub mod error; 6 | mod utils; 7 | 8 | pub use decimal_checked_ops::DecimalCheckedOps; 9 | 10 | mod decimal_checked_ops { 11 | use cosmwasm_std::{Decimal, Fraction, OverflowError, Uint128, Uint256}; 12 | use std::convert::TryInto; 13 | pub trait DecimalCheckedOps { 14 | fn checked_add(self, other: Decimal) -> Result; 15 | fn checked_mul_uint128(self, other: Uint128) -> Result; 16 | } 17 | 18 | impl DecimalCheckedOps for Decimal { 19 | fn checked_add(self, other: Decimal) -> Result { 20 | self.numerator() 21 | .checked_add(other.numerator()) 22 | .map(|_| self + other) 23 | } 24 | fn checked_mul_uint128(self, other: Uint128) -> Result { 25 | if self.is_zero() || other.is_zero() { 26 | return Ok(Uint128::zero()); 27 | } 28 | let multiply_ratio = 29 | other.full_mul(self.numerator()) / Uint256::from(self.denominator()); 30 | if multiply_ratio > Uint256::from(Uint128::MAX) { 31 | Err(OverflowError::new( 32 | cosmwasm_std::OverflowOperation::Mul, 33 | self, 34 | other, 35 | )) 36 | } else { 37 | Ok(multiply_ratio.try_into().unwrap()) 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/voting-escrow/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::{cw_serde, QueryResponses}; 2 | use cosmwasm_std::{Decimal, Uint128}; 3 | use cw20::{BalanceResponse, Cw20ReceiveMsg, TokenInfoResponse}; 4 | use crate::state::Point; 5 | 6 | #[cw_serde] 7 | pub struct InstantiateMsg { 8 | pub admin: String, 9 | pub guardian_addr: Option, 10 | pub deposit_token: String, 11 | } 12 | 13 | /// This structure describes the execute functions in the contract. 14 | #[cw_serde] 15 | pub enum ExecuteMsg { 16 | /// Extend the lockup time for your staked LP 17 | ExtendLockTime { time: u64 }, 18 | /// Receives a message of type [`Cw20ReceiveMsg`] and processes it depending on the received 19 | /// template. 20 | Receive(Cw20ReceiveMsg), 21 | /// Withdraw LP from the contract 22 | Withdraw {}, 23 | } 24 | 25 | /// This structure describes a CW20 hook message. 26 | #[cw_serde] 27 | pub enum Cw20HookMsg { 28 | /// Create a veSIDE position and lock LP for `time` amount of time 29 | CreateLock { time: u64 }, 30 | /// Deposit veSIDE in another user's LP position 31 | DepositFor { user: String }, 32 | /// Add more veSIDE to your LP position 33 | ExtendLockAmount {}, 34 | } 35 | 36 | // First category 37 | // Calls will be made to lp-token contract 38 | 39 | // Send { 40 | // contract: side1zn9r7u0rgxnwwh08pv2x9l2ut7fz6ya22remklstyfgunq9mhd2qktttm2, 41 | // amount: "100", 42 | // msg: Binary(CreateLock {time: }) 43 | // } 44 | 45 | // Send { 46 | // contract: side1zn9r7u0rgxnwwh08pv2x9l2ut7fz6ya22remklstyfgunq9mhd2qktttm2, 47 | // amount: "100", 48 | // msg: Binary(ExtendLockAmount {}) 49 | // } 50 | 51 | // Second category 52 | // Calls will be made to ve-token contract 53 | 54 | // ExtendLockTime { time: u64 }, 55 | 56 | // Withdraw {}, 57 | 58 | /// This structure describes the query messages available in the contract. 59 | #[cw_serde] 60 | #[derive(QueryResponses)] 61 | pub enum QueryMsg { 62 | /// Return the user's veSIDE balance 63 | #[returns(BalanceResponse)] 64 | Balance { address: String }, 65 | /// Fetch the veSIDE token information 66 | #[returns(TokenInfoResponse)] 67 | TokenInfo {}, 68 | /// Return the current total amount of veSIDE 69 | #[returns(VotingPowerResponse)] 70 | TotalVotingPower {}, 71 | /// Return the total amount of veSIDE at some point in the past 72 | #[returns(VotingPowerResponse)] 73 | TotalVotingPowerAt { time: u64 }, 74 | /// Return the total voting power at a specific period 75 | #[returns(VotingPowerResponse)] 76 | TotalVotingPowerAtPeriod { period: u64 }, 77 | /// Return the user's current voting power (veSIDE balance) 78 | #[returns(VotingPowerResponse)] 79 | UserVotingPower { user: String }, 80 | /// Return the user's veSIDE balance at some point in the past 81 | #[returns(VotingPowerResponse)] 82 | UserVotingPowerAt { user: String, time: u64 }, 83 | /// Return the user's voting power at a specific period 84 | #[returns(VotingPowerResponse)] 85 | UserVotingPowerAtPeriod { user: String, period: u64 }, 86 | /// Return information about a user's lock position 87 | #[returns(LockInfoResponse)] 88 | LockInfo { user: String }, 89 | /// Return user's locked LP balance at the given block height 90 | #[returns(Uint128)] 91 | UserDepositAtHeight { user: String, height: u64 }, 92 | /// Return the veSIDE contract configuration 93 | #[returns(ConfigResponse)] 94 | Config {}, 95 | /// Return the veSIDE amount for staking x amount of lp-token or adding some time 96 | #[returns(Point)] 97 | SimulateLock { 98 | user: String, 99 | add_amount: Option, 100 | time: Option, 101 | }, 102 | } 103 | 104 | /// This structure is used to return a user's amount of veSIDE. 105 | #[cw_serde] 106 | pub struct VotingPowerResponse { 107 | /// The veSIDE balance 108 | pub voting_power: Uint128, 109 | } 110 | 111 | /// This structure is used to return the lock information for a veSIDE position. 112 | #[cw_serde] 113 | pub struct LockInfoResponse { 114 | /// The amount of LP locked in the position 115 | pub amount: Uint128, 116 | /// This is the initial boost for the lock position 117 | pub coefficient: Decimal, 118 | /// Start time for the veSIDE position decay 119 | pub start: u64, 120 | /// End time for the veSIDE position decay 121 | pub end: u64, 122 | /// Slope at which a staker's veSIDE balance decreases over time 123 | pub slope: Uint128, 124 | } 125 | 126 | /// This structure stores the parameters returned when querying for a contract's configuration. 127 | #[cw_serde] 128 | pub struct ConfigResponse { 129 | pub admin: String, 130 | pub deposit_token: String, 131 | } 132 | 133 | /// This structure describes a Migration message. 134 | #[cw_serde] 135 | pub struct MigrateMsg {} 136 | -------------------------------------------------------------------------------- /contracts/voting-escrow/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | use cosmwasm_std::{Addr, Uint128}; 3 | use cw_storage_plus::{Item, Map, SnapshotMap, Strategy}; 4 | 5 | /// This structure stores the main parameters for the voting escrow contract. 6 | #[cw_serde] 7 | pub struct Config { 8 | /// Address that's allowed to change contract parameters 9 | pub admin: Addr, 10 | /// LP token contract address 11 | pub deposit_token: Addr, 12 | } 13 | 14 | /// This structure stores points along the checkpoint history for every LP staker. 15 | #[cw_serde] 16 | pub struct Point { 17 | /// The staker's veSIDE voting power 18 | pub power: Uint128, 19 | /// The start period when the staker's voting power start to decrease 20 | pub start: u64, 21 | /// The period when the lock should expire 22 | pub end: u64, 23 | /// Weekly voting power decay 24 | pub slope: Uint128, 25 | } 26 | 27 | /// This structure stores data about the lockup position for a specific LP staker. 28 | #[cw_serde] 29 | pub struct Lock { 30 | /// The total amount of LP tokens that were deposited in the LP position 31 | pub amount: Uint128, 32 | /// The start period when the lock was created 33 | pub start: u64, 34 | /// The timestamp when the lock position expires 35 | pub end: u64, 36 | /// the last period when the lock's time was increased 37 | pub last_extend_lock_period: u64, 38 | } 39 | 40 | /// Stores the contract config at the given key 41 | pub const CONFIG: Item = Item::new("config"); 42 | 43 | /// Stores all user lock history 44 | pub const LOCKED: SnapshotMap = SnapshotMap::new( 45 | "locked", 46 | "locked__checkpoints", 47 | "locked__changelog", 48 | Strategy::EveryBlock, 49 | ); 50 | 51 | /// Stores the checkpoint history for every staker (addr => period) 52 | /// Total voting power checkpoints are stored using a (contract_addr => period) key 53 | pub const HISTORY: Map<(Addr, u64), Point> = Map::new("history"); 54 | 55 | /// Scheduled slope changes per period (week) 56 | pub const SLOPE_CHANGES: Map = Map::new("slope_changes"); 57 | 58 | /// Last period when a scheduled slope change was applied 59 | pub const LAST_SLOPE_CHANGE: Item = Item::new("last_slope_change"); 60 | -------------------------------------------------------------------------------- /contracts/voting-escrow/src/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::error::ContractError; 2 | 3 | use cosmwasm_std::{Addr, Decimal, Order, StdError, StdResult, Storage, Uint128}; 4 | use cw_storage_plus::Bound; 5 | 6 | use crate::state::{Point, HISTORY, LAST_SLOPE_CHANGE, SLOPE_CHANGES}; 7 | 8 | /// Seconds in one week. It is intended for period number calculation. 9 | pub const WEEK: u64 = 7 * 86400; // lock period is rounded down by week 10 | 11 | /// Seconds in 2 years which is the maximum lock period. 12 | pub const MAX_LOCK_TIME: u64 = 2 * 365 * 86400; // 2 years (104 weeks) 13 | 14 | pub const EPOCH_START: u64 = 1705475751; 15 | 16 | /// Calculates the period number. Time should be formatted as a timestamp. 17 | pub fn get_period(time: u64) -> StdResult { 18 | if time < EPOCH_START { 19 | Err(StdError::generic_err("Invalid time")) 20 | } else { 21 | Ok((time - EPOCH_START) / WEEK) 22 | } 23 | } 24 | 25 | /// Calculates how many periods are in the specified time interval. The time should be in seconds. 26 | pub fn get_periods_count(interval: u64) -> u64 { 27 | interval / WEEK 28 | } 29 | 30 | /// Checks that a timestamp is within limits. 31 | pub(crate) fn time_limits_check(time: u64) -> Result<(), ContractError> { 32 | if !(WEEK..=MAX_LOCK_TIME).contains(&time) { 33 | Err(ContractError::LockTimeLimitsError {}) 34 | } else { 35 | Ok(()) 36 | } 37 | } 38 | 39 | /// Adjusting voting power according to the slope. The maximum loss is 103/104 * 104 which is 40 | /// 0.000103 LP. 41 | pub(crate) fn adjust_vp_and_slope(vp: &mut Uint128, dt: u64) -> StdResult { 42 | let slope = vp.checked_div(Uint128::from(dt))?; 43 | *vp = slope * Uint128::from(dt); 44 | Ok(slope) 45 | } 46 | 47 | /// Main function used to calculate a user's voting power at a specific period as: previous_power - slope*(x - previous_x). 48 | pub(crate) fn calc_voting_power(point: &Point, period: u64) -> Uint128 { 49 | let shift = point 50 | .slope 51 | .checked_mul(Uint128::from(period - point.start)) 52 | .unwrap_or_else(|_| Uint128::zero()); 53 | point 54 | .power 55 | .checked_sub(shift) 56 | .unwrap_or_else(|_| Uint128::zero()) 57 | } 58 | 59 | /// Coefficient calculation where 0 [`WEEK`] is equal to 1 and [`MAX_LOCK_TIME`] is 2.5. 60 | pub(crate) fn calc_coefficient(interval: u64) -> Decimal { 61 | // coefficient = 1 + 1.5 * (end - start) / MAX_LOCK_TIME 62 | Decimal::one() + Decimal::from_ratio(15_u64 * interval, get_periods_count(MAX_LOCK_TIME) * 10) 63 | } 64 | 65 | /// Fetches the last checkpoint in [`HISTORY`] for the given address. 66 | pub(crate) fn fetch_last_checkpoint( 67 | storage: &dyn Storage, 68 | addr: &Addr, 69 | period_key: u64, 70 | ) -> StdResult> { 71 | HISTORY 72 | .prefix(addr.clone()) 73 | .range( 74 | storage, 75 | None, 76 | Some(Bound::inclusive(period_key)), 77 | Order::Descending, 78 | ) 79 | .next() 80 | .transpose() 81 | } 82 | 83 | /// Cancels scheduled slope change of total voting power only if the given period is in future. 84 | /// Removes scheduled slope change if it became zero. 85 | pub(crate) fn cancel_scheduled_slope( 86 | storage: &mut dyn Storage, 87 | slope: Uint128, 88 | period: u64, 89 | ) -> StdResult<()> { 90 | let end_period_key = period; 91 | let last_slope_change = LAST_SLOPE_CHANGE.may_load(storage)?.unwrap_or(0); 92 | match SLOPE_CHANGES.may_load(storage, end_period_key)? { 93 | // We do not need to schedule a slope change in the past 94 | Some(old_scheduled_change) if period > last_slope_change => { 95 | let new_slope = old_scheduled_change - slope; 96 | if !new_slope.is_zero() { 97 | SLOPE_CHANGES.save(storage, end_period_key, &(old_scheduled_change - slope)) 98 | } else { 99 | SLOPE_CHANGES.remove(storage, end_period_key); 100 | Ok(()) 101 | } 102 | } 103 | _ => Ok(()), 104 | } 105 | } 106 | 107 | /// Schedules slope change of total voting power in the given period. 108 | pub(crate) fn schedule_slope_change( 109 | storage: &mut dyn Storage, 110 | slope: Uint128, 111 | period: u64, 112 | ) -> StdResult<()> { 113 | if !slope.is_zero() { 114 | SLOPE_CHANGES 115 | .update(storage, period, |slope_opt| -> StdResult { 116 | if let Some(pslope) = slope_opt { 117 | Ok(pslope + slope) 118 | } else { 119 | Ok(slope) 120 | } 121 | }) 122 | .map(|_| ()) 123 | } else { 124 | Ok(()) 125 | } 126 | } 127 | 128 | /// Fetches all slope changes between `last_slope_change` and `period`. 129 | pub(crate) fn fetch_slope_changes( 130 | storage: &dyn Storage, 131 | last_slope_change: u64, 132 | period: u64, 133 | ) -> StdResult> { 134 | SLOPE_CHANGES 135 | .range( 136 | storage, 137 | Some(Bound::exclusive(last_slope_change)), 138 | Some(Bound::inclusive(period)), 139 | Order::Ascending, 140 | ) 141 | .collect() 142 | } 143 | 144 | // /// Returns a lowercased, validated address upon success. Otherwise returns [`Err`] 145 | // /// ## Params 146 | // /// * **api** is an object of type [`Api`] 147 | // /// 148 | // /// * **addr** is an object of type [`impl Into`] 149 | // pub fn addr_validate_to_lower(api: &dyn Api, addr: impl Into) -> StdResult { 150 | // let addr = addr.into(); 151 | // if addr.to_lowercase() != addr { 152 | // return Err(StdError::generic_err(format!( 153 | // "Address {} should be lowercase", 154 | // addr 155 | // ))); 156 | // } 157 | // api.addr_validate(&addr) 158 | // } 159 | -------------------------------------------------------------------------------- /docs/ics101.md: -------------------------------------------------------------------------------- 1 | ## Synopsis ICS101 2 | 3 | This document specifies states, msgs and testcases for ics101 cosmwasm integration demo. 4 | For the detailed specification of ics101, please check [ics101 ibc spec document](). 5 | 6 | ### Motivation 7 | 8 | Ics101 go version is already developed and currently in the test and review phase. This cosmwasm integration is developed to interact between a chain that has integrated go version ics101 and a chain that doesn't have go version integration. 9 | It can be also used between the chains without ics101 integration, using this cosmwasm contract on each chain. 10 | 11 | ## Deployment 12 | 13 | ## Testing 14 | 15 | ## Run relayer between wasm chains -------------------------------------------------------------------------------- /ics100_swap.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sideprotocol/mesh-liquidity-wasm/6a7f5f1f7cd2344311f6bfe17ac0be41bfe782a2/ics100_swap.wasm -------------------------------------------------------------------------------- /schema/execute_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "ExecuteMsg", 4 | "oneOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "LogObservation" 9 | ], 10 | "properties": { 11 | "LogObservation": { 12 | "type": "object", 13 | "required": [ 14 | "token1", 15 | "token2" 16 | ], 17 | "properties": { 18 | "token1": { 19 | "$ref": "#/definitions/Coin" 20 | }, 21 | "token2": { 22 | "$ref": "#/definitions/Coin" 23 | } 24 | } 25 | } 26 | }, 27 | "additionalProperties": false 28 | }, 29 | { 30 | "type": "object", 31 | "required": [ 32 | "SetContract" 33 | ], 34 | "properties": { 35 | "SetContract": { 36 | "type": "object", 37 | "required": [ 38 | "address" 39 | ], 40 | "properties": { 41 | "address": { 42 | "type": "string" 43 | } 44 | } 45 | } 46 | }, 47 | "additionalProperties": false 48 | } 49 | ], 50 | "definitions": { 51 | "Coin": { 52 | "type": "object", 53 | "required": [ 54 | "amount", 55 | "denom" 56 | ], 57 | "properties": { 58 | "amount": { 59 | "$ref": "#/definitions/Uint128" 60 | }, 61 | "denom": { 62 | "type": "string" 63 | } 64 | } 65 | }, 66 | "Uint128": { 67 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 68 | "type": "string" 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /schema/instantiate_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "type": "object", 5 | "required": [ 6 | "contract", 7 | "max_length" 8 | ], 9 | "properties": { 10 | "contract": { 11 | "type": "string" 12 | }, 13 | "max_length": { 14 | "type": "integer", 15 | "format": "uint64", 16 | "minimum": 0.0 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /schema/query_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "QueryMsg", 4 | "oneOf": [ 5 | { 6 | "description": "Returns volume between specific interval Returns total volume till latest timestamp", 7 | "type": "object", 8 | "required": [ 9 | "total_volume" 10 | ], 11 | "properties": { 12 | "total_volume": { 13 | "type": "object" 14 | } 15 | }, 16 | "additionalProperties": false 17 | }, 18 | { 19 | "description": "Returns total volume till given timestamp", 20 | "type": "object", 21 | "required": [ 22 | "total_volume_at" 23 | ], 24 | "properties": { 25 | "total_volume_at": { 26 | "type": "object", 27 | "required": [ 28 | "timestamp" 29 | ], 30 | "properties": { 31 | "timestamp": { 32 | "type": "integer", 33 | "format": "uint64", 34 | "minimum": 0.0 35 | } 36 | } 37 | } 38 | }, 39 | "additionalProperties": false 40 | }, 41 | { 42 | "description": "Returns contract address for which volume is tracked", 43 | "type": "object", 44 | "required": [ 45 | "contract" 46 | ], 47 | "properties": { 48 | "contract": { 49 | "type": "object" 50 | } 51 | }, 52 | "additionalProperties": false 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /scripts/cancel-swap.sh: -------------------------------------------------------------------------------- 1 | HOME1="~/.wasmd1" 2 | CONTRACT1="wasm1466nf3zuxpya8q9emxukd7vftaf6h4psr0a07srl5zw74zh84yjqeam05w" 3 | CONTRACT2="wasm1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqm3x37s" 4 | KEY1="main1" 5 | CHAIN_ID1="source-chain" 6 | 7 | # Create make swap msg on source chain 8 | wasmd tx wasm execute $CONTRACT1 '{"CancelSwap": {"order_id": "66a661747fede287cc6f6439eb3597683ae973a9c1b738bd9430798ae71ff013", "maker_address": "wasm15f0j8n2pmet97zaztucpsnxgz7gmrtruvh5ayt", "creation_timestamp": "1683125169", "timeout_height": 200, "timeout_timestamp": 1683126169 }}' --from $KEY1 --keyring-backend test --home "$HOME1" --chain-id $CHAIN_ID1 --gas-prices 0.025stake --gas auto --gas-adjustment 1.3 --trace -------------------------------------------------------------------------------- /scripts/check-balance.sh: -------------------------------------------------------------------------------- 1 | HOME1="~/.wasmd1" 2 | HOME2="~/.wasmd2" 3 | CHAINID1="source-chain" 4 | CHAINID2="target-chain" 5 | KEY1="main1" 6 | KEY2="main2" 7 | ADDRESS1="wasm15f0j8n2pmet97zaztucpsnxgz7gmrtruvh5ayt" 8 | ADDRESS2="wasm19cmekqgu779n6hjpga7jyvl4gvrd8uhhksa27d" 9 | CONTRACT1="wasm1466nf3zuxpya8q9emxukd7vftaf6h4psr0a07srl5zw74zh84yjqeam05w" 10 | CONTRACT2="wasm1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqm3x37s" 11 | 12 | # show balances 13 | echo "Balance of main1 on source chain" 14 | wasmd q bank balances $ADDRESS1 --home $HOME1 --chain-id $CHAINID1 15 | echo "\nBalance of main1 on target chain" 16 | wasmd q bank balances $ADDRESS1 --home $HOME2 --chain-id $CHAINID2 17 | 18 | echo "\n\nBalance of main2 on source chain" 19 | wasmd q bank balances $ADDRESS2 --home $HOME1 --chain-id $CHAINID1 20 | echo "\nBalance of main2 on target chain" 21 | wasmd q bank balances $ADDRESS2 --home $HOME2 --chain-id $CHAINID2 22 | 23 | echo "\n\nBalance of contract1 on source chain" 24 | wasmd q bank balances $CONTRACT1 --home $HOME1 --chain-id $CHAINID1 25 | echo "\nBalance of contract2 on target chain" 26 | wasmd q bank balances $CONTRACT2 --home $HOME2 --chain-id $CHAINID2 27 | -------------------------------------------------------------------------------- /scripts/deploy-contract-malaga.sh: -------------------------------------------------------------------------------- 1 | wasmd keys add wallet --keyring-backend test 2 | wasmd keys add wallet2 --keyring-backend test 3 | source <(curl -sSL https://raw.githubusercontent.com/CosmWasm/testnets/master/malaga-420/defaults.env) 4 | JSON=$(jq -n --arg addr $(wasmd keys show -a wallet --keyring-backend test) '{"denom":"umlg","address":$addr}') && curl -X POST --header "Content-Type: application/json" --data "$JSON" https://faucet.malaga-420.cosmwasm.com/credit 5 | JSON=$(jq -n --arg addr $(wasmd keys show -a wallet2 --keyring-backend test) '{"denom":"umlg","address":$addr}') && curl -X POST --header "Content-Type: application/json" --data "$JSON" https://faucet.malaga-420.cosmwasm.com/credit 6 | NODE="--node $RPC" 7 | TXFLAG="${NODE} --chain-id ${CHAIN_ID} --gas-prices 0.25${FEE_DENOM} --gas auto --gas-adjustment 1.3" 8 | wasmd query bank total --node https://rpc.malaga-420.cosmwasm.com:443 9 | wasmd query bank balances $(wasmd keys show -a wallet --keyring-backend test) --node https://rpc.malaga-420.cosmwasm.com:443 10 | wasmd query bank balances $(wasmd keys show -a wallet2 --keyring-backend test) --node https://rpc.malaga-420.cosmwasm.com:443 11 | wasmd tx wasm store target/wasm32-unknown-unknown/release/ics100_swap.wasm --from wallet -y -b block --keyring-backend test --chain-id malaga-420 --node https://rpc.malaga-420.cosmwasm.com:443 --gas-prices 0.25umlg --gas auto --gas-adjustment 1.3 -------------------------------------------------------------------------------- /scripts/deploy-contract-wasmd.sh: -------------------------------------------------------------------------------- 1 | CHAIN_ID1="source-chain" 2 | HOME1="~/.wasmd1" 3 | CHAIN_ID2="target-chain" 4 | HOME2="~/.wasmd2" 5 | KEY1="main1" 6 | KEY2="main2" 7 | wasmd tx wasm store ../target/wasm32-unknown-unknown/release/ics100_swap.wasm --from $KEY1 -y -b block --keyring-backend test --home $HOME1 --chain-id $CHAIN_ID1 --gas-prices 0.025stake --gas auto --gas-adjustment 1.3 8 | wasmd tx wasm store ../target/wasm32-unknown-unknown/release/ics100_swap.wasm --from $KEY2 -y -b block --keyring-backend test --home $HOME2 --chain-id $CHAIN_ID2 --gas-prices 0.025stake --gas auto --gas-adjustment 1.3 -------------------------------------------------------------------------------- /scripts/details-query.sh: -------------------------------------------------------------------------------- 1 | HOME1="~/.wasmd1" 2 | HOME2="~/.wasmd2" 3 | CONTRACT1="wasm1466nf3zuxpya8q9emxukd7vftaf6h4psr0a07srl5zw74zh84yjqeam05w" 4 | CONTRACT2="wasm1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqm3x37s" 5 | LIST_QUERY='{"details": {"id":"66a661747fede287cc6f6439eb3597683ae973a9c1b738bd9430798ae71ff013"}}' 6 | echo "Query details in source chain" 7 | wasmd query wasm contract-state smart $CONTRACT1 "$LIST_QUERY" --output json --home $HOME1 8 | echo "\nQuery details in target chain" 9 | wasmd query wasm contract-state smart $CONTRACT2 "$LIST_QUERY" --output json --home $HOME2 -------------------------------------------------------------------------------- /scripts/init-contract-malaga.sh: -------------------------------------------------------------------------------- 1 | INIT='{}' 2 | CODE_ID="4848" 3 | LABEL="ics100 contract" 4 | KEY="wallet" 5 | 6 | wasmd tx wasm instantiate $CODE_ID "$INIT" --from $KEY --chain-id malaga-420 --label "$LABEL" --no-admin --keyring-backend test --node https://rpc.malaga-420.cosmwasm.com:443 --gas-prices 0.25umlg --gas auto --gas-adjustment 1.3 -y 7 | 8 | # wasmd query wasm list-contract-by-code $CODE2 --node https://rpc.malaga-420.cosmwasm.com:443 --chain-id malaga-420 --output json | jq -r '.contracts[-1]' -------------------------------------------------------------------------------- /scripts/init-contract-wasmd.sh: -------------------------------------------------------------------------------- 1 | INIT='{}' 2 | CHAIN_ID1="source-chain" 3 | HOME1="~/.wasmd1" 4 | CHAIN_ID2="target-chain" 5 | HOME2="~/.wasmd2" 6 | KEY1="main1" 7 | KEY2="main2" 8 | CODE1="7" 9 | CODE2="5" 10 | LABEL="ics100 contract" 11 | 12 | # Init ics100 contract on source chain 13 | wasmd tx wasm instantiate $CODE1 "$INIT" --from $KEY1 --chain-id $CHAIN_ID1 --label "$LABEL" --no-admin --keyring-backend test --home $HOME1 14 | # Init ics100 contract on target chain 15 | wasmd tx wasm instantiate $CODE2 "$INIT" --from $KEY2 --chain-id $CHAIN_ID2 --label "$LABEL" --no-admin --keyring-backend test --home $HOME2 -------------------------------------------------------------------------------- /scripts/list-swap.sh: -------------------------------------------------------------------------------- 1 | HOME1="~/.wasmd1" 2 | HOME2="~/.wasmd2" 3 | CONTRACT1="wasm1tqwwyth34550lg2437m05mjnjp8w7h5ka7m70jtzpxn4uh2ktsmqt0n86u" 4 | CONTRACT2="wasm1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrqr5j2ht" 5 | SWAPID="2708f44389952a0e75cf3e77cb926772668ba829866a00d4c5297a55466e46e7" 6 | LIST_QUERY='{"list": {}}' 7 | # echo "List of atomic swaps on source chain" 8 | # wasmd query wasm contract-state smart $CONTRACT1 "$LIST_QUERY" --output json --home $HOME1 9 | echo "\nList of atomic swaps on target chain" 10 | wasmd query wasm contract-state smart $CONTRACT2 "$LIST_QUERY" --output json --home $HOME2 11 | echo "\nList of atomic swaps on sidechain" 12 | sidechaind query ibc-swap orders -------------------------------------------------------------------------------- /scripts/make-swap-sidechain.sh: -------------------------------------------------------------------------------- 1 | HOME1="~/.wasmd1" 2 | HOME2="~/.wasmd2" 3 | KEY="bob" 4 | CHAIN_ID="sidechain_7070-1" 5 | CHANNEL_ID="channel-4" 6 | RECEIVER="wasm1fll0djfrcpkhszxzf4lfg6fp7eu6ywxqcn6pnx" 7 | 8 | sidechaind tx ibc-swap make $CHANNEL_ID 100aside $RECEIVER 100stake --from $KEY --keyring-backend test --chain-id $CHAIN_ID --gas-prices 0.01aside --gas auto --gas-adjustment 1.2 --trace -------------------------------------------------------------------------------- /scripts/make-swap-target.sh: -------------------------------------------------------------------------------- 1 | HOME1="~/.wasmd1" 2 | HOME2="~/.wasmd2" 3 | CONTRACT1="" 4 | CONTRACT2="wasm1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrqr5j2ht" 5 | KEY1="main1" 6 | KEY2="main2" 7 | CHAIN_ID1="source-chain" 8 | CHAIN_ID2="target-chain" 9 | 10 | # Create make swap msg on source chain 11 | wasmd tx wasm execute $CONTRACT2 '{"MakeSwap": {"source_port": "wasm.wasm1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrqr5j2ht", "source_channel": "channel-4", "sell_token": {"amount": "100", "denom": "token"}, "buy_token": { "amount": "100", "denom": "aside" }, "maker_address": "wasm1ts2jqyjjee9dxxhljchx2kg2y248qs85pfvle6", "maker_receiving_address": "side19kl420hmy0m9d0uul67kn20xnnkgkxmwg49rh9", "desired_taker":"", "create_timestamp": 1683279635, "expiration_timestamp": 1693399749000000000, "timeout_height": { "revision_number": 0, "revision_height": 99999999 }, "timeout_timestamp": 1693399799000000000 }}' --from $KEY2 --keyring-backend test --home "$HOME2" --chain-id $CHAIN_ID2 --gas-prices 0.025stake --gas auto --gas-adjustment 1.3 --amount 100token --trace -------------------------------------------------------------------------------- /scripts/make-swap.sh: -------------------------------------------------------------------------------- 1 | HOME1="~/.wasmd1" 2 | CONTRACT1="wasm1wn625s4jcmvk0szpl85rj5azkfc6suyvf75q6vrddscjdphtve8s5lsurx" 3 | CONTRACT2="wasm1eyfccmjm6732k7wp4p6gdjwhxjwsvje44j0hfx8nkgrm8fs7vqfsuw7sel" 4 | KEY1="main1" 5 | CHAIN_ID1="source-chain" 6 | 7 | # Create make swap msg on source chain 8 | wasmd tx wasm execute $CONTRACT1 '{"MakeSwap": {"source_port": "wasm.wasm1wn625s4jcmvk0szpl85rj5azkfc6suyvf75q6vrddscjdphtve8s5lsurx", "source_channel": "channel-6", "sell_token": { "native": [{ "amount": "100", "denom": "stake" }] }, "buy_token": { "native": [{"amount": "100", "denom": "token"}] }, "maker_address": "wasm15f0j8n2pmet97zaztucpsnxgz7gmrtruvh5ayt", "maker_receiving_address": "wasm15f0j8n2pmet97zaztucpsnxgz7gmrtruvh5ayt", "creation_timestamp": "1683279635000000000", "expiration_timestamp": "1683289635000000000", "timeout_height": 200, "timeout_timestamp": "1683289635000000000" }}' --from $KEY1 --keyring-backend test --home "$HOME1" --chain-id $CHAIN_ID1 --gas-prices 0.025stake --gas auto --gas-adjustment 1.3 --amount 100stake --trace -------------------------------------------------------------------------------- /scripts/rly-create-channel-with-sidechain.sh: -------------------------------------------------------------------------------- 1 | PATH_NAME="ics100_b" 2 | SRC_PORT="swap" 3 | DST_PORT="wasm.wasm1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrqr5j2ht" 4 | VERSION="ics100-1" 5 | 6 | # Create a new go relayer channel in the path 7 | rly tx channel $PATH_NAME --src-port "$SRC_PORT" --dst-port "$DST_PORT" --version "$VERSION" -------------------------------------------------------------------------------- /scripts/rly-create-channel.sh: -------------------------------------------------------------------------------- 1 | PATH_NAME="ics100" 2 | SRC_PORT="wasm.wasm1wn625s4jcmvk0szpl85rj5azkfc6suyvf75q6vrddscjdphtve8s5lsurx" 3 | DST_PORT="wasm.wasm1eyfccmjm6732k7wp4p6gdjwhxjwsvje44j0hfx8nkgrm8fs7vqfsuw7sel" 4 | VERSION="ics100-1" 5 | 6 | # Create a new go relayer channel in the path 7 | rly tx channel $PATH_NAME --src-port "wasm.osmo1usde2wnww8qp5f4gjquyw2nukgz70y3elttfqsvxvs9ur889yn7s8nt68s" --dst-port "wasm.juno1r65gaut7qamn36pnq3c290grcdrrlm3cahfc82lt0kg3lk0qel0qauz74g" --version "ics101-1" 8 | 9 | rly tx channel wasm-path --src-port "$SRC_PORT" --dst-port "$DST_PORT" --version "$VERSION" 10 | -------------------------------------------------------------------------------- /scripts/run-makerchain.sh: -------------------------------------------------------------------------------- 1 | CHAIN_ID1="source-chain" 2 | TXFLAG1="--chain-id $CHAIN_ID1 --gas-prices 0.025stake --gas auto --gas-adjustment 1.3" 3 | HOME1="~/.wasmd1" 4 | KEY_TEST="--keyring-backend test" 5 | wasmd init my-node --chain-id $CHAIN_ID1 --home $HOME1 6 | wasmd keys add main1 --keyring-backend test --home $HOME1 7 | wasmd keys add validator1 --keyring-backend test --home $HOME1 8 | wasmd add-genesis-account $(wasmd keys show main1 -a --keyring-backend test --home $HOME1) 10000000000stake --home $HOME1 --keyring-backend test 9 | wasmd add-genesis-account $(wasmd keys show validator1 -a --keyring-backend test --home $HOME1) 10000000000stake --home $HOME1 --keyring-backend test 10 | wasmd gentx validator1 1000000000stake --chain-id $CHAIN_ID1 --home $HOME1 --keyring-backend test 11 | wasmd collect-gentxs --home $HOME1 12 | wasmd validate-genesis --home $HOME1 13 | wasmd start --home $HOME1 -------------------------------------------------------------------------------- /scripts/run-takerchain.sh: -------------------------------------------------------------------------------- 1 | CHAIN_ID2="target-chain" 2 | TXFLAG2="--chain-id $CHAIN_ID2 --gas-prices 0.025stake --gas auto --gas-adjustment 1.3" 3 | HOME2="~/.wasmd2" 4 | KEY_TEST="--keyring-backend test" 5 | wasmd init my-node --chain-id $CHAIN_ID2 --home $HOME2 6 | wasmd keys add main2 --keyring-backend test --home $HOME2 7 | wasmd keys add validator2 --keyring-backend test --home $HOME2 8 | wasmd add-genesis-account $(wasmd keys show main2 -a --keyring-backend test --home $HOME2) 10000000000stake,1000000000000token --home $HOME2 --keyring-backend test 9 | wasmd add-genesis-account $(wasmd keys show validator2 -a --keyring-backend test --home $HOME2) 10000000000stake,1000000000000token --home $HOME2 --keyring-backend test 10 | wasmd gentx validator2 1000000000stake --chain-id $CHAIN_ID2 --home $HOME2 --keyring-backend test 11 | wasmd collect-gentxs --home $HOME2 12 | wasmd validate-genesis --home $HOME2 13 | # wasmd start --home $HOME2 -------------------------------------------------------------------------------- /scripts/sidechain.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "cosmos", 3 | "value": { 4 | "key": "bob", 5 | "chain-id": "sidechain_7070-1", 6 | "rpc-addr": "http://localhost:26657", 7 | "account-prefix": "side", 8 | "keyring-backend": "test", 9 | "extra-codecs": ["ethermint"], 10 | "gas-adjustment": 1.2, 11 | "gas-prices": "0.01aside", 12 | "debug": true, 13 | "timeout": "20s", 14 | "output-format": "json", 15 | "sign-mode": "direct" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /scripts/source.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "cosmos", 3 | "value": { 4 | "key": "main1", 5 | "chain-id": "source-chain", 6 | "rpc-addr": "http://localhost:26657", 7 | "account-prefix": "wasm", 8 | "keyring-backend": "test", 9 | "gas-adjustment": 1.3, 10 | "gas-prices": "0.025stake", 11 | "debug": true, 12 | "timeout": "20s", 13 | "output-format": "json", 14 | "sign-mode": "direct" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /scripts/take-swap-sidechain.sh: -------------------------------------------------------------------------------- 1 | HOME1="~/.wasmd1" 2 | HOME2="~/.wasmd2" 3 | CHAIN_ID="sidechain_7070-1" 4 | SWAPID="294139cd2bfd6952010184a376a5a675834c8469045609f5f5b192a5e4b8f380" 5 | KEY1="main1" 6 | KEY2="main2" 7 | BOB="bob" 8 | 9 | sidechaind tx ibc-swap take $SWAPID 100aside wasm1fll0djfrcpkhszxzf4lfg6fp7eu6ywxqcn6pnx --from $BOB --keyring-backend test --chain-id $CHAIN_ID --gas-prices 0.01aside --gas auto --gas-adjustment 1.2 --trace --packet-timeout-height "0-9999994" --packet-timeout-timestamp "1693399799000000000" -------------------------------------------------------------------------------- /scripts/take-swap-source.sh: -------------------------------------------------------------------------------- 1 | HOME1="~/.wasmd1" 2 | HOME2="~/.wasmd2" 3 | CONTRACT1="wasm1tqwwyth34550lg2437m05mjnjp8w7h5ka7m70jtzpxn4uh2ktsmqt0n86u" 4 | CONTRACT2="wasm1pvrwmjuusn9wh34j7y520g8gumuy9xtl3gvprlljfdpwju3x7ucsfg5rpz" 5 | CHAINID1="source-chain" 6 | CHAINID2="target-chain" 7 | SWAPID="a056de0b35273253bb5999ed095d466f044fef36348fc10da898909cb0d65d41" 8 | KEY1="main1" 9 | KEY2="main2" 10 | wasmd tx wasm execute $CONTRACT1 '{"TakeSwap": { "order_id": "a056de0b35273253bb5999ed095d466f044fef36348fc10da898909cb0d65d41", "sell_token": { "native": [{"amount": "100", "denom": "stake"}] }, "taker_address": "wasm15f0j8n2pmet97zaztucpsnxgz7gmrtruvh5ayt", "taker_receiving_address": "wasm15f0j8n2pmet97zaztucpsnxgz7gmrtruvh5ayt", "creation_timestamp": "1683279635000000000", "timeout_height": 200, "timeout_timestamp": "1693399749000000000" }}' --from $KEY1 --keyring-backend test --home $HOME1 --chain-id $CHAINID1 --gas-prices 0.025stake --gas auto --gas-adjustment 1.3 --amount 100stake --trace -------------------------------------------------------------------------------- /scripts/take-swap-target.sh: -------------------------------------------------------------------------------- 1 | HOME1="~/.wasmd1" 2 | HOME2="~/.wasmd2" 3 | CONTRACT2="wasm1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrqr5j2ht" 4 | CHAINID1="source-chain" 5 | CHAINID2="target-chain" 6 | SWAPID="42b3d7998f40c595cb14615ec68e5aa5f4083ae4d33f94b19adcb67281e88ea3" 7 | KEY1="main1" 8 | KEY2="main2" 9 | wasmd tx wasm execute $CONTRACT2 '{"TakeSwap": { "order_id": "42b3d7998f40c595cb14615ec68e5aa5f4083ae4d33f94b19adcb67281e88ea3", "sell_token": {"amount": "100", "denom": "stake"}, "taker_address": "wasm1ts2jqyjjee9dxxhljchx2kg2y248qs85pfvle6", "taker_receiving_address": "side19kl420hmy0m9d0uul67kn20xnnkgkxmwg49rh9", "create_timestamp": 1683279635, "timeout_height": { "revision_number": 0, "revision_height": 99999999 }, "timeout_timestamp": 1693399799000000000 }}' --from $KEY2 --keyring-backend test --home $HOME2 --chain-id $CHAINID2 --gas-prices 0.025stake --gas auto --gas-adjustment 1.3 --amount 100stake --trace -------------------------------------------------------------------------------- /scripts/take-swap.sh: -------------------------------------------------------------------------------- 1 | HOME1="~/.wasmd1" 2 | HOME2="~/.wasmd2" 3 | CONTRACT1="wasm1wn625s4jcmvk0szpl85rj5azkfc6suyvf75q6vrddscjdphtve8s5lsurx" 4 | CONTRACT2="wasm1eyfccmjm6732k7wp4p6gdjwhxjwsvje44j0hfx8nkgrm8fs7vqfsuw7sel" 5 | CHAINID1="source-chain" 6 | CHAINID2="target-chain" 7 | SWAPID="825e9cfb35eac2d3e6b8add9dc0134308e7c3fbc067b46e43c86503996a48c60" 8 | KEY2="main2" 9 | wasmd tx wasm execute $CONTRACT2 '{"TakeSwap": { "order_id": "825e9cfb35eac2d3e6b8add9dc0134308e7c3fbc067b46e43c86503996a48c60", "sell_token": { "native": [{"amount": "100", "denom": "token"}] }, "taker_address": "wasm19cmekqgu779n6hjpga7jyvl4gvrd8uhhksa27d", "taker_receiving_address": "wasm19cmekqgu779n6hjpga7jyvl4gvrd8uhhksa27d", "creation_timestamp": "1683279635000000000", "timeout_height": 200, "timeout_timestamp": "1683289635000000000" }}' --from $KEY2 --keyring-backend test --home $HOME2 --chain-id $CHAINID2 --gas-prices 0.025stake --gas auto --gas-adjustment 1.3 --amount 100token --trace -------------------------------------------------------------------------------- /scripts/target.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "cosmos", 3 | "value": { 4 | "key": "main2", 5 | "chain-id": "target-chain", 6 | "rpc-addr": "http://localhost:26667", 7 | "account-prefix": "wasm", 8 | "keyring-backend": "test", 9 | "gas-adjustment": 1.3, 10 | "gas-prices": "0.025stake", 11 | "debug": true, 12 | "timeout": "20s", 13 | "output-format": "json", 14 | "sign-mode": "direct" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /testnet-configurations/ics100.md: -------------------------------------------------------------------------------- 1 | # ICS100 Contract deployments 2 | 3 | ## Osmosis 4 | 5 | - Contract Address: `osmo1ap4d6l8et2axqx6rez8u63l9h5m0e85020h59j4nfqn03cauyh3slvzxej` 6 | - RPC: https://rpc.osmotest5.osmosis.zone:443 7 | - Chain ID: osmo-test-5 8 | 9 | ## Neutron 10 | 11 | - Contract Address: `neutron14f2ppy55ssfutsxpvx8clrw4xvfvum3gp8x792wtgmk9xgvqlq2qhwmlj8` 12 | - RPC: https://rpc-palvus.pion-1.ntrn.tech/ 13 | - Chain ID: pion-1 14 | 15 | # Relayer Configuration 16 | 17 | ``` 18 | // ADD HERE 19 | ``` -------------------------------------------------------------------------------- /testnet-configurations/ics101.md: -------------------------------------------------------------------------------- 1 | # ICS101 Contract deployments 2 | 3 | ## Osmosis 4 | 5 | - Contract Address: `osmo1yj3cm4j5rd9z089054j6cdle7du5xl3dhs8c3uhtqctw2p5cmhxs3gvjhl` 6 | - RPC: https://rpc.osmotest5.osmosis.zone:443 7 | - Chain ID: osmo-test-5 8 | 9 | ## Juno 10 | 11 | - Contract Address: `juno1k7z4px2r09g8alxg5x528lk58efrthkhw6lp0wk7llcz00rk9qpqck8s92` 12 | - RPC: https://uni-rpc.reece.sh:443 13 | - Chain ID: uni-6 14 | 15 | # Relayer Configuration 16 | 17 | ## Connection Osmosis <--> Juno 18 | ``` 19 | src: 20 | chain-id: osmo-test-5 21 | client-id: 07-tendermint-447 22 | connection-id: connection-409 23 | dst: 24 | chain-id: uni-6 25 | client-id: 07-tendermint-463 26 | connection-id: connection-604 27 | src-channel-filter: 28 | rule: allowlist 29 | channel-list: 30 | - channel-927 31 | - channel-531 32 | ``` --------------------------------------------------------------------------------