Skip to content

Commit

Permalink
Refine interfaces for token transfer (#37)
Browse files Browse the repository at this point in the history
* Implement QueryErc20TokenBalance

* Implement CanQueryTokenBalance for StarknetChain

* Test query balance

* Implement CanTransferToken for StarknetChain

* Use transfer_token in test

* Use variables for addresses in test

* Remove unused patch.crates-io

* Implement InvokeContractMessageBuilder

* Implement CanSendMessages for StarknetChain

* Implement CanSubmitTx for StarknetChain

* Use message senders to invoke contract

* Implement QueryTransactionReceipt

* Implement CanPollTxResponse for StarknetChain

* Use poll_tx_response in message sender
  • Loading branch information
soareschen authored Jul 31, 2024
1 parent dfb2d7a commit f74bae4
Show file tree
Hide file tree
Showing 32 changed files with 500 additions and 115 deletions.
29 changes: 4 additions & 25 deletions relayer/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions relayer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ ibc-client-tendermint = { git = "https://github.com/cosmos/ibc-rs.git", rev = "5
ibc-client-wasm-types = { git = "https://github.com/cosmos/ibc-rs.git", rev = "5dc0b09" }
ibc-app-transfer = { git = "https://github.com/cosmos/ibc-rs.git", rev = "5dc0b09" }
ibc-primitives = { git = "https://github.com/cosmos/ibc-rs.git", rev = "5dc0b09" }
ibc-query = { git = "https://github.com/cosmos/ibc-rs.git", rev = "5dc0b09" }
ibc-derive = { git = "https://github.com/cosmos/ibc-rs.git", rev = "5dc0b09" }

cgp-core = { git = "https://github.com/informalsystems/cgp.git" }
Expand Down Expand Up @@ -111,15 +110,10 @@ hermes-logger = { git = "https://github.com/informalsystems/
hermes-test-components = { git = "https://github.com/informalsystems/hermes-sdk.git" }
hermes-ibc-test-suite = { git = "https://github.com/informalsystems/hermes-sdk.git" }

hermes-cli = { git = "https://github.com/informalsystems/hermes-sdk.git" }
hermes-cli-components = { git = "https://github.com/informalsystems/hermes-sdk.git" }
hermes-cli-framework = { git = "https://github.com/informalsystems/hermes-sdk.git" }

hermes-any-counterparty = { git = "https://github.com/informalsystems/hermes-sdk.git" }

hermes-cosmos-chain-components = { git = "https://github.com/informalsystems/hermes-sdk.git" }
hermes-cosmos-relayer = { git = "https://github.com/informalsystems/hermes-sdk.git" }
hermes-cosmos-wasm-relayer = { git = "https://github.com/informalsystems/hermes-sdk.git" }
hermes-cosmos-test-components = { git = "https://github.com/informalsystems/hermes-sdk.git" }
hermes-cosmos-integration-tests = { git = "https://github.com/informalsystems/hermes-sdk.git" }

Expand Down
55 changes: 55 additions & 0 deletions relayer/crates/starknet-chain-components/src/components.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
use cgp_core::prelude::*;
pub use hermes_relayer_components::chain::traits::send_message::MessageSenderComponent;
pub use hermes_relayer_components::chain::traits::types::event::EventTypeComponent;
pub use hermes_relayer_components::chain::traits::types::message::MessageTypeComponent;
use hermes_relayer_components::error::impls::retry::ReturnRetryable;
pub use hermes_relayer_components::error::traits::retry::RetryableErrorComponent;
pub use hermes_relayer_components::transaction::impls::poll_tx_response::PollTimeoutGetterComponent;
use hermes_relayer_components::transaction::impls::poll_tx_response::PollTxResponse;
pub use hermes_relayer_components::transaction::traits::poll_tx_response::TxResponsePollerComponent;
pub use hermes_relayer_components::transaction::traits::query_tx_response::TxResponseQuerierComponent;
pub use hermes_relayer_components::transaction::traits::submit_tx::TxSubmitterComponent;
pub use hermes_relayer_components::transaction::traits::types::transaction::TransactionTypeComponent;
pub use hermes_relayer_components::transaction::traits::types::tx_hash::TransactionHashTypeComponent;
pub use hermes_relayer_components::transaction::traits::types::tx_response::TxResponseTypeComponent;

use crate::impls::contract::call::CallStarknetContract;
use crate::impls::contract::invoke::InvokeStarknetContract;
use crate::impls::contract::message::BuildInvokeContractCall;
use crate::impls::queries::token_balance::QueryErc20TokenBalance;
use crate::impls::send_message::SendCallMessages;
use crate::impls::submit_tx::SubmitCallTransaction;
use crate::impls::transfer::TransferErc20Token;
use crate::impls::tx_response::{DefaultPollTimeout, QueryTransactionReceipt};
use crate::impls::types::address::ProvideFeltAddressType;
use crate::impls::types::amount::ProvideU256Amount;
use crate::impls::types::blob::ProvideFeltBlobType;
use crate::impls::types::event::ProvideDummyEvent;
use crate::impls::types::message::ProvideCallMessage;
use crate::impls::types::method::ProvideFeltMethodSelector;
use crate::impls::types::transaction::ProvideCallTransaction;
use crate::impls::types::tx_hash::ProvideFeltTxHash;
use crate::impls::types::tx_response::ProvideTransactionReceipt;
pub use crate::traits::contract::call::ContractCallerComponent;
pub use crate::traits::contract::invoke::ContractInvokerComponent;
pub use crate::traits::contract::message::InvokeContractMessageBuilderComponent;
pub use crate::traits::queries::token_balance::TokenBalanceQuerierComponent;
pub use crate::traits::transfer::TokenTransferComponent;
pub use crate::traits::types::address::AddressTypeComponent;
pub use crate::traits::types::amount::AmountTypeComponent;
pub use crate::traits::types::blob::BlobTypeComponent;
pub use crate::traits::types::method::MethodSelectorTypeComponent;

Expand All @@ -19,13 +46,41 @@ define_components! {
ProvideFeltAddressType,
BlobTypeComponent:
ProvideFeltBlobType,
MessageTypeComponent:
ProvideCallMessage,
EventTypeComponent:
ProvideDummyEvent,
AmountTypeComponent:
ProvideU256Amount,
TransactionTypeComponent:
ProvideCallTransaction,
TransactionHashTypeComponent:
ProvideFeltTxHash,
TxResponseTypeComponent:
ProvideTransactionReceipt,
MethodSelectorTypeComponent:
ProvideFeltMethodSelector,
MessageSenderComponent:
SendCallMessages,
TxSubmitterComponent:
SubmitCallTransaction,
TxResponseQuerierComponent:
QueryTransactionReceipt,
TxResponsePollerComponent:
PollTxResponse,
PollTimeoutGetterComponent:
DefaultPollTimeout,
ContractCallerComponent:
CallStarknetContract,
ContractInvokerComponent:
InvokeStarknetContract,
InvokeContractMessageBuilderComponent:
BuildInvokeContractCall,
TokenBalanceQuerierComponent:
QueryErc20TokenBalance,
TokenTransferComponent:
TransferErc20Token,
RetryableErrorComponent:
ReturnRetryable<false>,
}
}
Original file line number Diff line number Diff line change
@@ -1,48 +1,23 @@
use hermes_relayer_components::transaction::traits::types::tx_hash::HasTransactionHashType;
use starknet::accounts::{Account, Call};
use starknet::core::types::Felt;
use hermes_relayer_components::chain::traits::send_message::CanSendSingleMessage;

use crate::traits::account::{CanRaiseAccountErrors, HasStarknetAccount};
use crate::traits::contract::invoke::ContractInvoker;
use crate::traits::provider::HasStarknetProvider;
use crate::traits::types::address::HasAddressType;
use crate::traits::types::blob::HasBlobType;
use crate::traits::types::method::HasMethodSelectorType;
use crate::traits::contract::message::CanBuildInvokeContractMessage;

pub struct InvokeStarknetContract;

impl<Chain> ContractInvoker<Chain> for InvokeStarknetContract
where
Chain: HasAddressType<Address = Felt>
+ HasMethodSelectorType<MethodSelector = Felt>
+ HasBlobType<Blob = Vec<Felt>>
+ HasTransactionHashType<TxHash = Felt>
+ HasStarknetProvider
+ HasStarknetAccount
+ CanRaiseAccountErrors,
Chain: CanBuildInvokeContractMessage + CanSendSingleMessage,
{
async fn invoke_contract(
chain: &Chain,
contract_address: &Felt,
entry_point_selector: &Felt,
calldata: &Vec<Felt>,
) -> Result<Felt, Chain::Error> {
let account = chain.account();
contract_address: &Chain::Address,
entry_point_selector: &Chain::MethodSelector,
calldata: &Chain::Blob,
) -> Result<Vec<Chain::Event>, Chain::Error> {
let message =
chain.build_invoke_contract_message(contract_address, entry_point_selector, calldata);

let call = Call {
to: *contract_address,
selector: *entry_point_selector,
calldata: calldata.clone(),
};

let execution = account.execute_v3(vec![call]);

let tx_hash = execution
.send()
.await
.map_err(Chain::raise_error)?
.transaction_hash;

Ok(tx_hash)
chain.send_message(message).await
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use hermes_relayer_components::chain::traits::types::message::HasMessageType;
use starknet::accounts::Call;
use starknet::core::types::Felt;

use crate::traits::contract::message::InvokeContractMessageBuilder;
use crate::traits::types::address::HasAddressType;
use crate::traits::types::blob::HasBlobType;
use crate::traits::types::method::HasMethodSelectorType;

pub struct BuildInvokeContractCall;

impl<Chain> InvokeContractMessageBuilder<Chain> for BuildInvokeContractCall
where
Chain: HasAddressType<Address = Felt>
+ HasMethodSelectorType<MethodSelector = Felt>
+ HasBlobType<Blob = Vec<Felt>>
+ HasMessageType<Message = Call>,
{
fn build_invoke_contract_message(
_chain: &Chain,
contract_address: &Felt,
entry_point_selector: &Felt,
calldata: &Vec<Felt>,
) -> Call {
Call {
to: *contract_address,
selector: *entry_point_selector,
calldata: calldata.clone(),
}
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod call;
pub mod invoke;
pub mod message;
5 changes: 5 additions & 0 deletions relayer/crates/starknet-chain-components/src/impls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
pub mod account;
pub mod contract;
pub mod provider;
pub mod queries;
pub mod send_message;
pub mod submit_tx;
pub mod transfer;
pub mod tx_response;
pub mod types;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod token_balance;
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use cgp_core::error::CanRaiseError;
use starknet::core::types::{Felt, U256};
use starknet::macros::selector;

use crate::traits::contract::call::CanCallContract;
use crate::traits::queries::token_balance::TokenBalanceQuerier;
use crate::traits::types::address::HasAddressType;
use crate::traits::types::amount::HasAmountType;
use crate::traits::types::blob::HasBlobType;
use crate::traits::types::method::HasMethodSelectorType;

pub struct QueryErc20TokenBalance;

pub const BALANCE_SELECTOR: Felt = selector!("balance_of");

impl<Chain> TokenBalanceQuerier<Chain> for QueryErc20TokenBalance
where
Chain: HasAddressType<Address = Felt>
+ HasAmountType<Amount = U256>
+ HasBlobType<Blob = Vec<Felt>>
+ HasMethodSelectorType<MethodSelector = Felt>
+ CanCallContract
+ CanRaiseError<&'static str>,
{
async fn query_token_balance(
chain: &Chain,
token_address: &Felt,
account_address: &Felt,
) -> Result<U256, Chain::Error> {
let output = chain
.call_contract(token_address, &BALANCE_SELECTOR, &vec![*account_address])
.await?;

let [e1, e2]: [Felt; 2] = output.try_into().map_err(|_| {
Chain::raise_error(
"expect output returned from balance_of query to be consist of two felt252 values",
)
})?;

let low = u128::from_be_bytes(e1.to_bytes_be()[16..].try_into().unwrap());
let high = u128::from_be_bytes(e2.to_bytes_be()[16..].try_into().unwrap());

let amount = U256::from_words(low, high);

Ok(amount)
}
}
32 changes: 32 additions & 0 deletions relayer/crates/starknet-chain-components/src/impls/send_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use cgp_core::error::HasErrorType;
use hermes_relayer_components::chain::traits::send_message::MessageSender;
use hermes_relayer_components::chain::traits::types::event::HasEventType;
use hermes_relayer_components::chain::traits::types::message::HasMessageType;
use hermes_relayer_components::transaction::traits::poll_tx_response::CanPollTxResponse;
use hermes_relayer_components::transaction::traits::submit_tx::CanSubmitTx;
use starknet::accounts::Call;

pub struct SendCallMessages;

impl<Chain> MessageSender<Chain> for SendCallMessages
where
Chain: HasMessageType<Message = Call>
+ CanSubmitTx<Transaction = Vec<Call>>
+ CanPollTxResponse
+ HasEventType
+ HasErrorType,
{
async fn send_messages(
chain: &Chain,
messages: Vec<Call>,
) -> Result<Vec<Vec<Chain::Event>>, Chain::Error> {
// stub events
let events = messages.iter().map(|_| Vec::new()).collect();

let tx_hash = chain.submit_tx(&messages).await?;

chain.poll_tx_response(&tx_hash).await?;

Ok(events)
}
}
33 changes: 33 additions & 0 deletions relayer/crates/starknet-chain-components/src/impls/submit_tx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use hermes_relayer_components::transaction::traits::submit_tx::TxSubmitter;
use hermes_relayer_components::transaction::traits::types::transaction::HasTransactionType;
use hermes_relayer_components::transaction::traits::types::tx_hash::HasTransactionHashType;
use starknet::accounts::{Account, Call};
use starknet::core::types::Felt;

use crate::traits::account::{CanRaiseAccountErrors, HasStarknetAccount};
use crate::traits::provider::HasStarknetProvider;

pub struct SubmitCallTransaction;

impl<Chain> TxSubmitter<Chain> for SubmitCallTransaction
where
Chain: HasTransactionType<Transaction = Vec<Call>>
+ HasTransactionHashType<TxHash = Felt>
+ HasStarknetProvider
+ HasStarknetAccount
+ CanRaiseAccountErrors,
{
async fn submit_tx(chain: &Chain, messages: &Vec<Call>) -> Result<Felt, Chain::Error> {
let account = chain.account();

let execution = account.execute_v3(messages.clone());

let tx_hash = execution
.send()
.await
.map_err(Chain::raise_error)?
.transaction_hash;

Ok(tx_hash)
}
}
Loading

0 comments on commit f74bae4

Please sign in to comment.