diff --git a/README.md b/README.md index d4725043..ce50f57b 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ To gain a better understanding of how ICS721 (interchain) workflows function, co - Mint an NFT. - Transfer the NFT from one chain to another. +For testing interchain transfers please check [gist code snippet](https://gist.github.com/taitruong/c561fbebc46a99b3723d37a1dc3ff0af). + ## From a thousand feet up This contract deals in debt-vouchers. @@ -78,11 +80,27 @@ These sorts of issues can cause trouble with relayer implementations. The inabil ## Callbacks -There are 2 types of callbacks users can use when transfering an NFT: +cw-ics721 supports [callbacks](./packages/ics721-types/src/types.rs#L67-L70) for Ics721ReceiveCallback and Ics721AckCallback. 1. Receive callback - Callback that is being called on the receiving chain when the NFT was succesfully transferred. 2. Ack callback - Callback that is being called on the sending chain notifying about the status of the transfer. +Workflow: + +1. `send_nft` from cw721 -> cw-ics721. +2. `send_nft` holds `IbcOutgoingMsg` msg. +3. `IbcOutgoingMsg` holds `Ics721Memo` with optional receive (request) and ack (response) callbacks. +4. `cw-ics721` on target chain executes optional receive callback. +5. `cw-ics721` sends ack success or ack error to `cw-ics721` on source chain. +6. `cw-ics721` on source chain executes optional ack callback. + +NOTES: + +In case of 4. if any error occurs on target chain, NFT gets rolled back and return to sender on source chain. +In case of 6. ack callback also holds `Ics721Status::Success` or `Ics721Status::Failed(String)` + +### Callback Execution + Callbacks are optional and can be added in the memo field of the transfer message: ```json @@ -96,8 +114,91 @@ Callbacks are optional and can be added in the memo field of the transfer messag } ``` +An [Ics721Memo](./packages/ics721-types/src/types.rs#L11-L30) may be provided as part of [IbcOutgoingMsg](./packages/ics721-types/src/ibc_types.rs#L99): + +```rust +// -- ibc_types.rs +#[cw_serde] +pub struct IbcOutgoingMsg { + /// The address that should receive the NFT being sent on the + /// *receiving chain*. + pub receiver: String, + /// The *local* channel ID this ought to be sent away on. This + /// contract must have a connection on this channel. + pub channel_id: String, + /// Timeout for the IBC message. + pub timeout: IbcTimeout, + /// Memo to add custom string to the msg + pub memo: Option, +} + +// -- types.rs +pub struct Ics721Memo { + pub callbacks: Option, +} + +/// The format we expect for the memo field on a send +#[cw_serde] +pub struct Ics721Callbacks { + /// Data to pass with a callback on source side (status update) + /// Note - If this field is empty, no callback will be sent + pub ack_callback_data: Option, + /// The address that will receive the callback message + /// Defaults to the sender address + pub ack_callback_addr: Option, + /// Data to pass with a callback on the destination side (ReceiveNftIcs721) + /// Note - If this field is empty, no callback will be sent + pub receive_callback_data: Option, + /// The address that will receive the callback message + /// Defaults to the receiver address + pub receive_callback_addr: Option, +} + +``` + In order to execute an ack callback, `ack_callback_data` must not be empty. In order to execute a receive callback, `receive_callback_data` must not be empty. +A contract sending an NFT with callback may look like this: + +```rust +let callback_msg = MyAckCallbackMsgData { + // ... any arbitrary data contract wants to +}; +let mut callbacks = Ics721Callbacks { + ack_callback_data: Some(to_json_binary(&callback_msg)?), + ack_callback_addr: None, // in case of none ics721 uses recipient (default) as callback addr + receive_callback_data: None, + receive_callback_addr: None, +}; +if let Some(counterparty_contract) = COUNTERPARTY_CONTRACT.may_load(deps.storage)? { + callbacks.receive_callback_data = Some(to_json_binary(&callback_msg)?); + callbacks.receive_callback_addr = Some(counterparty_contract); // here we need to set contract addr, since receiver is NFT receiver +} +let memo = Ics721Memo { + callbacks: Some(callbacks), +}; +let ibc_msg = IbcOutgoingMsg { + receiver, + channel_id, + timeout: IbcTimeout::with_timestamp(env.block.time.plus_minutes(30)), + memo: Some(Binary::to_base64(&to_json_binary(&memo)?)), +}; +// send nft to ics721 (or outgoing proxy if set by ics721) +let send_nft_msg = Cw721ExecuteMsg::SendNft { + contract: 'ADDR_ICS721_OUTGOING_PROXY'.to_string(), + token_id: token_id.to_string(), + msg: to_json_binary(&ibc_msg)?, +}; +let send_nft_sub_msg = SubMsg::::reply_on_success( + WasmMsg::Execute { + contract_addr: CW721_ADDR.load(storage)?.to_string(), + msg: to_json_binary(&send_nft_msg)?, + funds: vec![], + }, + REPLY_NOOP, +); +``` + ### Contract to accept callbacks In order for a contract to accept callbacks, it must implement the next messages: diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..36217999 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,45 @@ +# Setup ICS721 + +For ICS721 it requires these contracts: + +- ICS721: the bridge itself +- Incoming Proxy: optional contract for filtering incoming packets +- Outgoing Proxy: optional contract for filtering incoming packets + +NOTE: +Below scripts use [select-chain.sh](./select-chain.sh). For each selected chain there is an `.env` file like `stargaze.env` and `osmosis.env`. + +## Scripts + +### Initial Setup + +Scripts for setup must be executed in this order: + +1. ICS721 without proxies: [instantiate-ics721.sh](./instantiate-ics721.sh) +2. Incoming Proxy: [instantiate-incoming-proxy.sh](./instantiate-incoming-proxy.sh) +3. Outgoing Proxy: [instantiate-outgoing-proxy.sh](.instantiate-outgoing-proxy.sh) + +After instantiation: + +- update `ADDR_ICS721`, `ADDR_INCOMING_PROXY`, `ADDR_OUTGOING_PROXY` in env file +- Note: ICS721 is instantiated without(!) proxies, proxies are added via migration (velow) + +### Migration + +1. ICS721 : [migrate-ics721.sh](./migrate-ics721.sh) +2. Incoming Proxy: [migrate-incoming-proxy.sh](./migrate-incoming-proxy.sh) +3. Outgoing Proxy: [migrate-outgoing-proxy.sh](.migrate-outgoing-proxy.sh) + +### Outgoing Proxy Messages + +Usage: + +```sh +$ ./scripts/whitelist-outgoing-proxy.sh +Usage: ./scripts/whitelist-outgoing-proxy.sh stargaze|osmosis [--add WHITELIST|--remove WHITELIST|--enable true_or_false] --type collection|channel|checksum|fees +Example: +./scripts/whitelist-outgoing-proxy.sh stargaze|osmosis --add channel-1 --type channel +./scripts/whitelist-outgoing-proxy.sh stargaze|osmosis --enable false --type collection +``` + +The owner of the outgoing proxy contract can add, remove and enable whitelists for collections, channels, collection checksums, and collection fees. diff --git a/scripts/instantiate-ics721.sh b/scripts/instantiate-ics721.sh new file mode 100755 index 00000000..157e3042 --- /dev/null +++ b/scripts/instantiate-ics721.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# ---------------------------------------------------- +# Instantiates the ICS721 contract with cw721_base_code_id and pauser +# ---------------------------------------------------- + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +# select chain +if [[ -z "$CHAIN" ]]; then + source "$SCRIPT_DIR"/select-chain.sh + CHAIN=$(select_chain) + export CHAIN +fi +echo "reading $SCRIPT_DIR/$CHAIN.env" +source "$SCRIPT_DIR"/"$CHAIN".env + +printf -v MSG '{"cw721_base_code_id": %s, "pauser": "%s"}' $CODE_ID_CW721 $WALLET_OWNER +CMD="$CLI tx wasm instantiate $CODE_ID_ICS721 '$MSG' --label 'ICS721 with rate limiter outgoing proxy'" +CMD+=" --from $WALLET --admin $WALLET_ADMIN" +CMD+=" --gas $CLI_GAS --gas-prices $CLI_GAS_PRICES --gas-adjustment $CLI_GAS_ADJUSTMENT" +CMD+=" --chain-id $CHAIN_ID --node $CHAIN_NODE -y" + +echo "executing: $CMD" >&2 +eval $CMD \ No newline at end of file diff --git a/scripts/instantiate-incoming-proxy.sh b/scripts/instantiate-incoming-proxy.sh new file mode 100755 index 00000000..d8982d68 --- /dev/null +++ b/scripts/instantiate-incoming-proxy.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# ---------------------------------------------------- +# Instantiates the Incoming Whitelist Channel Proxy contract +# with the whitelist channels and reference to the ICS721 contract +# ---------------------------------------------------- + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +# select chain +if [[ -z "$CHAIN" ]]; then + source "$SCRIPT_DIR"/select-chain.sh + CHAIN=$(select_chain) + export CHAIN +fi +echo "reading $SCRIPT_DIR/$CHAIN.env" +source "$SCRIPT_DIR"/"$CHAIN".env + +printf -v MSG '{"origin": "%s", "channels": %s}' $ADDR_ICS721 $CHANNELS +LABEL="ICS721 Incoming Whitelist Channel Proxy, Managed by Ark Protocol" +CMD="$CLI tx wasm instantiate $CODE_ID_INCOMING_PROXY '$MSG'" +CMD+=" --label '$LABEL'" +CMD+=" --from $WALLET --admin $WALLET_ADMIN" +CMD+=" --gas $CLI_GAS --gas-prices $CLI_GAS_PRICES --gas-adjustment $CLI_GAS_ADJUSTMENT" +CMD+=" --chain-id $CHAIN_ID --node $CHAIN_NODE -y" + +echo "executing: $CMD" >&2 +eval $CMD \ No newline at end of file diff --git a/scripts/instantiate-outgoing-proxy.sh b/scripts/instantiate-outgoing-proxy.sh new file mode 100755 index 00000000..e2609937 --- /dev/null +++ b/scripts/instantiate-outgoing-proxy.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# ---------------------------------------------------- +# Instantiates the ICS721 Outgoing Whitelist Channel Proxy contract +# with the (channels, collections, checksums, collection fees) whitelist and reference to the ICS721 contract +# ---------------------------------------------------- + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +# select chain +if [[ -z "$CHAIN" ]]; then + source "$SCRIPT_DIR"/select-chain.sh + CHAIN=$(select_chain) + export CHAIN +fi +echo "reading $SCRIPT_DIR/$CHAIN.env" +source "$SCRIPT_DIR"/"$CHAIN".env + +MSG=$( + cat <&2 +eval $CMD \ No newline at end of file diff --git a/scripts/migrate-ics721.sh b/scripts/migrate-ics721.sh new file mode 100755 index 00000000..7b85dc64 --- /dev/null +++ b/scripts/migrate-ics721.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# ---------------------------------------------------- +# Migrates the ICS721 contract, and sets optional: +# - incoming proxy +# - outgoing proxy +# - cw721 code id +# - pauser +# - cw721 admin +# ---------------------------------------------------- + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +# select chain +if [[ -z "$CHAIN" ]]; then + source "$SCRIPT_DIR"/select-chain.sh + CHAIN=$(select_chain) + export CHAIN +fi +echo "reading $SCRIPT_DIR/$CHAIN.env" +source "$SCRIPT_DIR"/"$CHAIN".env + +function query_config() { + echo "cw721 code id: $($CLI query wasm contract-state smart $ADDR_ICS721 '{"cw721_code_id": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "cw721 admin: $($CLI query wasm contract-state smart $ADDR_ICS721 '{"cw721_admin": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "outgoing proxy: $($CLI query wasm contract-state smart $ADDR_ICS721 '{"outgoing_proxy": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "incoming proxy: $($CLI query wasm contract-state smart $ADDR_ICS721 '{"incoming_proxy": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "pauser: $($CLI query wasm contract-state smart $ADDR_ICS721 '{"pauser": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "contract: $($CLI query wasm contract $ADDR_ICS721 --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 +} + +echo "==========================================================================================" >&2 +echo "configs before migration $ADDR_ICS721:" >&2 +query_config + +echo "==========================================================================================" >&2 +echo "!!! migrating $ADDR_ICS721 data: proxy: $ADDR_OUTGOING_PROXY, cw721 code id: $CODE_ID_CW721 !!!" >&2 # use CW721 if not set +MSG=$( + cat <&2 +eval $CMD +if [ $? -ne 0 ]; then + echo "failed to migrate $ADDR_ICS721" >&2 + exit 1 +fi + +echo "==========================================================================================" >&2 +echo "configs after migration $ADDR_ICS721:" >&2 +sleep 10 +query_config diff --git a/scripts/migrate-incoming-proxy.sh b/scripts/migrate-incoming-proxy.sh new file mode 100755 index 00000000..1db1c1ec --- /dev/null +++ b/scripts/migrate-incoming-proxy.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# ---------------------------------------------------- +# Migrate the Incoming Whitelist Channel Proxy contract, and sets: +# - origin +# - optional channels +# ---------------------------------------------------- + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +# select chain +if [[ -z "$CHAIN" ]]; then + source "$SCRIPT_DIR"/select-chain.sh + CHAIN=$(select_chain) + export CHAIN +fi +echo "reading $SCRIPT_DIR/$CHAIN.env" +source "$SCRIPT_DIR"/"$CHAIN".env + +function query_config() { + echo "contract: $($CLI query wasm contract $ADDR_INCOMING_PROXY --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "origin: $($CLI query wasm contract-state smart $ADDR_INCOMING_PROXY '{"get_origin": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + CURRENT_CHANNELS=$($CLI query wasm contract-state smart $ADDR_INCOMING_PROXY '{"get_channels": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq) + echo "channels: $CURRENT_CHANNELS" >&2 +} + +echo "==========================================================================================" >&2 +echo "configs before migration $ADDR_INCOMING_PROXY:" >&2 +query_config + +echo "==========================================================================================" >&2 +echo "Do you want to migrate channels?" >&2 +echo "current channels whitelist: $CURRENT_CHANNELS" >&2 +echo "Channels from config: $CHANNELS" >&2 +select yn in "Yes" "No"; do + case $yn in + Yes) + MIGRATE_CHANNELS=true + OPTION_ONE=", \"channels\": $CHANNELS" + break + ;; + No) + MIGRATE_CHANNELS=false + OPTION_ONE="" + break + ;; + *) + echo "Please select Yes or No." >&2 + ;; + esac +done + +printf -v MSG '{ + "with_update": { + "origin": "%s" + %s + } +}' \ + "$ADDR_ICS721" \ + "$OPTION_ONE" + +CMD="$CLI tx wasm migrate $ADDR_INCOMING_PROXY $CODE_ID_INCOMING_PROXY '$MSG'" +CMD+=" --from $WALLET_ADMIN" +CMD+=" --gas-prices $CLI_GAS_PRICES --gas $CLI_GAS --gas-adjustment $CLI_GAS_ADJUSTMENT" +CMD+=" --chain-id $CHAIN_ID --node $CHAIN_NODE -y" + +echo "executing: $CMD" >&2 +eval $CMD +if [ $? -ne 0 ]; then + echo "failed to migrate $ADDR_ICS721" >&2 + exit 1 +fi + +echo "==========================================================================================" >&2 +echo "configs after migration $ADDR_INCOMING_PROXY:" >&2 +sleep 10 +query_config diff --git a/scripts/migrate-outgoing-proxy.sh b/scripts/migrate-outgoing-proxy.sh new file mode 100755 index 00000000..887a8aa3 --- /dev/null +++ b/scripts/migrate-outgoing-proxy.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# ---------------------------------------------------- +# Migrate the ICS721 Outgoing Whitelist Channel Proxy contract, and sets: +# - config with origin (ICS721) and owner +# - optional channels +# - optional collections +# - proxy with optional channels and collections whitelist +# ---------------------------------------------------- + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +# select chain +if [[ -z "$CHAIN" ]]; then + source "$SCRIPT_DIR"/select-chain.sh + CHAIN=$(select_chain) + export CHAIN +fi +echo "reading $SCRIPT_DIR/$CHAIN.env" +source "$SCRIPT_DIR"/"$CHAIN".env + +function query_config() { + echo "config: $($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_config": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "contract: $($CLI query wasm contract $ADDR_OUTGOING_PROXY --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "rate limit: $($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_rate_limit": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + CURRENT_CHANNELS=$($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_channels_whitelist": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq) + echo "channels whitelist: $CURRENT_CHANNELS" >&2 + CURRENT_COLLECTIONS=$($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_collections_whitelist": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq) + echo "collections whitelist: $CURRENT_COLLECTIONS" >&2 + echo "fees collection map: $($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_fees_collection_map": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + CURRENT_COLLECTION_CHECKSUMS=$($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_collection_checksums_whitelist": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq) + echo "collection checksums whitelist: $CURRENT_COLLECTION_CHECKSUMS" >&2 +} + +echo "==========================================================================================" >&2 +echo "configs before migration $ADDR_OUTGOING_PROXY:" >&2 +query_config + +echo "==========================================================================================" >&2 +echo "Do you want to migrate channels?" >&2 +echo "current channels whitelist: $CURRENT_CHANNELS" >&2 +echo "Channels from config: $CHANNELS" >&2 +select yn in "Yes" "No"; do + case $yn in + Yes) + MIGRATE_CHANNELS=true + OPTION_ONE=", \"channels\": $CHANNELS" + break + ;; + No) + MIGRATE_CHANNELS=false + OPTION_ONE="" + break + ;; + *) + echo "Please select Yes or No." >&2 + ;; + esac +done + +echo "Do you want to migrate collections?" >&2 +echo "current collections whitelist: $CURRENT_COLLECTIONS" >&2 +echo "Collection from config: $COLLECTIONS" >&2 +select yn in "Yes" "No"; do + case $yn in + Yes) + MIGRATE_COLLECTIONS=true + OPTION_TWO=", \"collections\": $COLLECTIONS" + break + ;; + No) + MIGRATE_COLLECTIONS=false + OPTION_TWO="" + break + ;; + *) + echo "Please select Yes or No." >&2 + ;; + esac +done + +printf -v MSG '{ + "with_update": { + "config": { + "origin": "%s", + "owner": "%s" + }, + "proxy": { + %s + %s + } + } +}' \ + "$ADDR_ICS721" \ + "$WALLET_OWNER" \ + "$OPTION_ONE" \ + "$OPTION_TWO" + +CMD="$CLI tx wasm migrate $ADDR_OUTGOING_PROXY $CODE_ID_OUTGOING_PROXY '$MSG'" +CMD+=" --from $WALLET_ADMIN" +CMD+=" --gas-prices $CLI_GAS_PRICES --gas $CLI_GAS --gas-adjustment $CLI_GAS_ADJUSTMENT" +CMD+=" --chain-id $CHAIN_ID --node $CHAIN_NODE -y" + +echo "executing: $CMD" >&2 +eval $CMD +if [ $? -ne 0 ]; then + echo "failed to migrate $ADDR_ICS721" >&2 + exit 1 +fi + +echo "==========================================================================================" >&2 +echo "configs after migration $ADDR_OUTGOING_PROXY:" >&2 +query_config diff --git a/scripts/osmosis.env b/scripts/osmosis.env new file mode 100644 index 00000000..f7d9e3db --- /dev/null +++ b/scripts/osmosis.env @@ -0,0 +1,36 @@ +export CLI=osmosisd +export CLI_OUTPUT=json +export CLI_DENOM=uosmo +export CLI_GAS=auto +export CLI_GAS_PRICE=0.1 +export CLI_GAS_PRICES=$CLI_GAS_PRICE$CLI_DENOM +export CLI_GAS_ADJUSTMENT=1.1 +export CLI_BROADCAST_MODE=sync + +export CHAIN=osmosis +export CHAIN_ID=osmo-test-5 # osmosis-1 +export CHAIN_NODE=https://osmosis-testnet-rpc.polkachu.com:443 # https://osmosis-rpc.polkachu.com:443 + +# wallets +export WALLET=YOUR_WALLET_ADDR # wallet executed the tx +# - owner of contract +# - ics721: used for pauser being able to pause ics721 +# - incoming and outgoing proxy: used for owner to execute msgs +export WALLET_OWNER=WALLET_OWNER +# - admin of contract for migrating contract +export WALLET_ADMIN=WALLET_ADMIN + +export CODE_ID_CW721=CODE_ID_CW721 +export CODE_ID_ICS721=CODE_ID_ICS721 +export ADDR_ICS721=ADDR_ICS721 + +# proxies +export CODE_ID_INCOMING_PROXY=7786 +export ADDR_INCOMING_PROXY=ADDR_INCOMING_PROXY +export CODE_ID_OUTGOING_PROXY=7788 +export ADDR_OUTGOING_PROXY=ADDR_OUTGOING_PROXY + +# Proxy configs +# - channels: used as WLs for incoming and outgoing proxy +export CHANNELS="[\"channel-1\",\"channel-2\"]" +export COLLECTIONS="[\"ADDR1\",\"ADDR2\"]" diff --git a/scripts/select-chain.sh b/scripts/select-chain.sh new file mode 100755 index 00000000..d83750c7 --- /dev/null +++ b/scripts/select-chain.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# ---------------------------------------------------- +# - exports CHAIN based on user input - +# ---------------------------------------------------- +export CHAIN_CHOICES=("stargaze" "osmosis") +export CHAIN_CHOICES_STR=$( + IFS=\| + echo "${CHAIN_CHOICES[*]}" +) + +function select_chain() { + # Read chains from the file into an array + echo "Available chains: ${CHAIN_CHOICES[*]}" >&2 + + echo "Please select the chain:" >&2 + select SELECTED_CHAIN in "${CHAIN_CHOICES[@]}" "Exit"; do + case $SELECTED_CHAIN in + "Exit") echo "Exiting..." >&2; return 0 ;; + *) if [[ " ${CHAIN_CHOICES[*]} " =~ " ${SELECTED_CHAIN} " ]]; then + echo "Selected chain: $SELECTED_CHAIN" >&2 + export CHAIN="$SELECTED_CHAIN" + break + else + echo "Invalid choice. Please try again." >&2 + fi ;; + esac + done + + export CHAIN="$SELECTED_CHAIN" + echo $CHAIN +} + +export -f select_chain \ No newline at end of file diff --git a/scripts/stargaze.env b/scripts/stargaze.env new file mode 100644 index 00000000..f399eccd --- /dev/null +++ b/scripts/stargaze.env @@ -0,0 +1,36 @@ +export CLI=starsd +export CLI_OUTPUT=json +export CLI_DENOM=ustars +export CLI_GAS=auto +export CLI_GAS_PRICE=1.2 +export CLI_GAS_PRICES=$CLI_GAS_PRICE$CLI_DENOM +export CLI_GAS_ADJUSTMENT=1.1 +export CLI_BROADCAST_MODE=sync + +export CHAIN=stargaze +export CHAIN_ID=elgafar-1 # stargaze-1 +export CHAIN_NODE=https://rpc.elgafar-1.stargaze-apis.com:443 # https://stargaze-rpc.polkachu.com:443 https://rpc.stargaze-apis.com:443 https://stargaze-rpc.publicnode.com:443 + +# wallets +export WALLET=YOUR_WALLET_ADDR # wallet executed the tx +# - owner of contract +# - ics721: used for pauser being able to pause ics721 +# - incoming and outgoing proxy: used for owner to execute msgs +export WALLET_OWNER=WALLET_OWNER +# - admin of contract for migrating contract +export WALLET_ADMIN=WALLET_ADMIN + +export CODE_ID_CW721=CODE_ID_CW721 +export CODE_ID_ICS721=CODE_ID_ICS721 +export ADDR_ICS721=ADDR_ICS721 + +# proxies +export CODE_ID_INCOMING_PROXY=7786 +export ADDR_INCOMING_PROXY=ADDR_INCOMING_PROXY +export CODE_ID_OUTGOING_PROXY=7788 +export ADDR_OUTGOING_PROXY=ADDR_OUTGOING_PROXY + +# Proxy configs +# - channels: used as WLs for incoming and outgoing proxy +export CHANNELS="[\"channel-1\",\"channel-2\"]" +export COLLECTIONS="[\"ADDR1\",\"ADDR2\"]" diff --git a/scripts/whitelist-outgoing-proxy.sh b/scripts/whitelist-outgoing-proxy.sh new file mode 100755 index 00000000..76d76a2f --- /dev/null +++ b/scripts/whitelist-outgoing-proxy.sh @@ -0,0 +1,159 @@ +#!/bin/bash +# ---------------------------------------------------- +# execute messages on the outgoing proxy contract, for whitelist management: +# - add/remove collection +# - add/remove channel +# - add/remove collection checksum +# - enable/disable collection|channel|checksum|fees +# ---------------------------------------------------- + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +set -o pipefail + +# get CHAIN_CHOICES +source "$SCRIPT_DIR"/select-chain.sh + +function query_config() { + echo "config: $($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_config": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE --output json | jq)" >&2 + echo "contract: $($CLI query wasm contract $ADDR_OUTGOING_PROXY --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "rate limit: $($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_rate_limit": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "channels whitelist: $($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_channels_whitelist": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "collections whitelist: $($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_collections_whitelist": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "collection checkums whitelist: $($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_collection_checksums_whitelist": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 + echo "fees collection map: $($CLI query wasm contract-state smart $ADDR_OUTGOING_PROXY '{"get_fees_collection_map": {}}' --chain-id $CHAIN_ID --node $CHAIN_NODE | jq)" >&2 +} + +ACTION="" +CHAIN="" +VALUE="" +TYPE="" + +prev_arg="" +for arg in "$@"; do + case $arg in + --add | --remove | --enable) + ACTION="$arg" + ;; + --fees) ;; + --type) ;; + collection | channel | fees | checksum | rate-limit) + if [[ "$prev_arg" != "--type" ]]; then + echo "Invalid argument order. Use --type before specifying 'collection' or 'channel'." >&2 + exit 1 + fi + TYPE="$arg" + ;; + *) + if [[ " ${CHAIN_CHOICES[*]} " =~ " $arg " ]]; then + CHAIN="$arg" + elif [[ "$prev_arg" == "--generate-only" || "$prev_arg" == "--broadcast" ]]; then + FILE="$arg" + elif [[ "$prev_arg" == "--add" || "$prev_arg" == "--remove" || "$prev_arg" == "--enable" ]]; then + VALUE="$arg" + elif [[ "$prev_arg" == "--fees" ]]; then + FEES="$arg" + else + echo "Invalid argument: $arg" >&2 + echo "Usage: $0 ${CHAIN_CHOICES_STR// /|} [--add WHITELIST|--remove WHITELIST|--enable true_or_false] --type collection|channel|checksum|fees" >&2 + exit 1 + fi + ;; + esac + prev_arg="$arg" +done + +# Check if all required arguments are provided +if [[ -z "$ACTION" || -z "$CHAIN" || -z "$VALUE" ]]; then + echo "Usage: $0 ${CHAIN_CHOICES_STR// /|} [--add WHITELIST|--remove WHITELIST|--enable true_or_false] --type collection|channel|checksum|fees|rate-limit" >&2 + echo "Example:" >&2 + echo "$0 ${CHAIN_CHOICES_STR// /|} --add channel-1 --type channel" >&2 + echo "$0 ${CHAIN_CHOICES_STR// /|} --enable false --type collection" >&2 + exit 1 +fi + +echo "reading $SCRIPT_DIR/$CHAIN.env" +source "$SCRIPT_DIR"/"$CHAIN".env + +case $ACTION in +--add) + if [ "$TYPE" == "collection" ]; then + printf -v MSG '{"update_config": {"add_collection_to_whitelist": {"collection": "%s"}}}' $VALUE + elif [ "$TYPE" == "channel" ]; then + printf -v MSG '{"update_config": {"add_channel_to_whitelist": {"channel": "%s"}}}' $VALUE + elif [ "$TYPE" == "fees" ]; then + printf -v MSG '{"update_config": {"add_fees_collection": {"collection": "%s", "fees": {"amount": "%s", "denom": "%s"}}}}' $VALUE $FEES $CLI_DENOM + elif [ "$TYPE" == "checksum" ]; then + printf -v MSG '{"update_config": {"add_collection_checksum_to_whitelist": {"checksum": "%s"}}}' $VALUE + else + echo "Invalid type $TYPE. Use collection, channel, fees, or checksum." >&2 + exit 1 + fi + ;; +--remove) + if [ "$TYPE" == "collection" ]; then + printf -v MSG '{"update_config": {"remove_collection_from_whitelist": {"collection": "%s"}}}' $VALUE + elif [ "$TYPE" == "channel" ]; then + printf -v MSG '{"update_config": {"remove_channel_from_whitelist": {"channel": "%s"}}}' $VALUE + elif [ "$TYPE" == "fees" ]; then + printf -v MSG '{"update_config": {"remove_fees_collection": {"collection": "%s"}}}' $VALUE + elif [ "$TYPE" == "checksum" ]; then + printf -v MSG '{"update_config": {"remove_collection_checksum_from_whitelist": {"checksum": "%s"}}}' $VALUE + else + echo "Invalid type $TYPE. Use collection, channel, fees, or checksum." >&2 + exit 1 + fi + ;; +--enable) + if [ "$TYPE" == "collection" ]; then + printf -v MSG '{"update_config": {"enable_collections_whitelist": %s}}' $VALUE + elif [ "$TYPE" == "channel" ]; then + printf -v MSG '{"update_config": {"enable_channels_whitelist": %s}}' $VALUE + elif [ "$TYPE" == "fees" ]; then + printf -v MSG '{"update_config": {"enable_fees_collections_whitelist": %s}}' $VALUE + elif [ "$TYPE" == "checksum" ]; then + printf -v MSG '{"update_config": {"enable_collection_checksums_whitelist": %s}}' $VALUE + elif [ "$TYPE" == "rate-limit" ]; then + printf -v MSG '{"update_config": {"enable_rate_limiter": %s}}' $VALUE + else + echo "Invalid type $TYPE. Use collection, channel, fees, checksum, or rate-limit." >&2 + exit 1 + fi + ;; +*) + echo "Invalid action $ACTION. Use --add, --remove, or --enable." >&2 + exit 1 + ;; +esac + +echo "==========================================================================================" >&2 +echo "=============== updating $ADDR_OUTGOING_PROXY using owner wallet $WALLET_OWNER" >&2 +CMD="$CLI tx wasm execute $ADDR_OUTGOING_PROXY '$MSG'" +CMD+=" --from $WALLET_OWNER" +CMD+=" --gas $CLI_GAS --gas-prices $CLI_GAS_PRICES --gas-adjustment $CLI_GAS_ADJUSTMENT" +CMD+=" --chain-id $CHAIN_ID --node $CHAIN_NODE -y" +echo "$CMD" >&2 +echo "Execute TX?" >&2 +select yn in "Yes" "No"; do + case $yn in + Yes) + break + ;; + No) + exit 0 + ;; + *) + echo "Please select Yes or No." >&2 + ;; + esac +done +eval $CMD +ERROR_CODE=${PIPESTATUS[0]} +if [ $ERROR_CODE -ne 0 ]; then + echo "ERROR executing" >&2 + exit $ERROR_CODE +fi + +echo "==========================================================================================" >&2 +echo "config after change $ADDR_OUTGOING_PROXY=$ADDR_OUTGOING_PROXY:" >&2 +sleep 10 +query_config diff --git a/ts-relayer-tests/build.sh b/ts-relayer-tests/build.sh index bcef98c7..ca76ecb4 100755 --- a/ts-relayer-tests/build.sh +++ b/ts-relayer-tests/build.sh @@ -11,7 +11,7 @@ cd "$(git rev-parse --show-toplevel)" docker run --rm -v "$(pwd)":/code --platform linux/amd64 \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/workspace-optimizer:0.15.0 + cosmwasm/workspace-optimizer:0.15.1 mkdir -p ./ts-relayer-tests/internal cp ./artifacts/*.wasm ./ts-relayer-tests/internal