Skip to content

Commit

Permalink
feat(cvm): intercept(percentage amounts and slippage) usage in exchan…
Browse files Browse the repository at this point in the history
…ge (#4257)

So can use any amount, max from virtual wallet will be taken. No
slippage support for now.

Required for merge:
- [ ] `pr-workflow-check / draft-release-check` is ✅ success
- Other rules GitHub shows you, or can be read in
[configuration](../terraform/github.com/branches.tf)

Makes review faster:
- [ ] PR title is my best effort to provide summary of changes and has
clear text to be part of release notes
- [ ] I marked PR by `misc` label if it should not be in release notes
- [ ] Linked Zenhub/Github/Slack/etc reference if one exists
- [ ] I was clear on what type of deployment required to release my
changes (node, runtime, contract, indexer, on chain operation, frontend,
infrastructure) if any in PR title or description
- [ ] Added reviewer into `Reviewers`
- [ ] I tagged(`@`) or used other form of notification of one person who
I think can handle best review of this PR
- [ ] I have proved that PR has no general regressions of relevant
features and processes required to release into production
- [ ] Any dependency updates made, was done according guides from
relevant dependency
- Clicking all checkboxes 
- Adding detailed description of changes when it feels appropriate (for
example when PR is big)
  • Loading branch information
dzmitry-lahoda authored Oct 28, 2023
1 parent c69c27c commit 6afbb34
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 28 deletions.
34 changes: 24 additions & 10 deletions code/xcvm/cosmwasm/contracts/interpreter/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,20 +182,34 @@ fn interpret_exchange(
};
ensure_eq!(give.0.len(), 1, ContractError::OnlySingleAssetExchangeIsSupportedByPool);
ensure_eq!(want.0.len(), 1, ContractError::OnlySingleAssetExchangeIsSupportedByPool);

let give = give.0[0].clone();
let want = want.0[0].clone();

let asset = gateway_address
.get_asset_by_id(deps.querier, give.0[0].0)
.get_asset_by_id(deps.querier, give.0)
.map_err(ContractError::AssetNotFound)?;
let give: xc_core::cosmos::Coin = xc_core::cosmos::Coin {
denom: asset.denom(),
amount: give.0[0].1.amount.intercept.to_string(),
};

let amount = deps.querier.query_balance(&sender, asset.denom())?;
let amount = give.1.amount.apply(amount.amount.u128())?;
let give: xc_core::cosmos::Coin =
xc_core::cosmos::Coin { denom: asset.denom(), amount: amount.to_string() };

let asset = gateway_address
.get_asset_by_id(deps.querier, want.0[0].0)
.get_asset_by_id(deps.querier, want.0)
.map_err(ContractError::AssetNotFound)?;
let want = xc_core::cosmos::Coin {
denom: asset.denom(),
amount: want.0[0].1.amount.intercept.to_string(),
};

if want.1.amount.is_both() {
return Err(ContractError::CannotDefineBothSlippageAndLimitAtSameTime)
}

if want.1.amount.is_ratio() {
return Err(ContractError::ExchangeDoesNotSupportSlippage)
}

let want =
xc_core::cosmos::Coin { denom: asset.denom(), amount: want.1.amount.intercept.to_string() };

let response = match exchange.exchange {
OsmosisCrossChainSwap(routes) => {
let msg = MsgSwapExactAmountIn {
Expand Down
6 changes: 6 additions & 0 deletions code/xcvm/cosmwasm/contracts/interpreter/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ pub enum ContractError {
#[error("Only single asset exchange is supported by pool")]
OnlySingleAssetExchangeIsSupportedByPool,

#[error("Exchange does not support slippage")]
ExchangeDoesNotSupportSlippage,

#[error("Cannot define both slippage and limit at same time")]
CannotDefineBothSlippageAndLimitAtSameTime,

#[error("Asset not found: {0}")]
AssetNotFound(StdError),
#[error("Exchange not found: {0}")]
Expand Down
28 changes: 28 additions & 0 deletions code/xcvm/lib/core/src/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ impl Amount {
self.slope.0 == 0
}

pub const fn is_both(&self) -> bool {
self.is_absolute() && self.is_ratio()
}

/// Helper function to see if the amount is ratio
pub const fn is_ratio(&self) -> bool {
self.intercept.0 == 0
Expand Down Expand Up @@ -339,6 +343,30 @@ pub fn generate_network_prefixed_id(network_id: NetworkId, protocol_id: u32, non
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn amounts() {
let amount = Amount::new(0, Amount::MAX_PARTS);
let result = amount.apply(100).unwrap();
assert_eq!(result, 100);

let amount = Amount::new(42, Amount::MAX_PARTS);
let result = amount.apply(100).unwrap();
assert_eq!(result, 100);

let amount = Amount::new(123, 0);
let result = amount.apply(100).unwrap();
assert_eq!(result, 100, "seems this is feature to ask more but return what is here");

let amount = Amount::new(42, 0);
let result = amount.apply(100).unwrap();
assert_eq!(result, 42);

let amount = Amount::new(50, Amount::MAX_PARTS / 10);
let result = amount.apply(100).unwrap();
assert_eq!(result, 50 + 5, "percentage of remaining");
}

#[test]
fn devnet() {
let pica_on_picasso = generate_asset_id(0.into(), 0, 1);
Expand Down
43 changes: 25 additions & 18 deletions docs/docs/technology/cvm/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ During program interpretation, while the instruction is executing, the result of

`Non-custodial` - each user owns all assets on each chain; they delegate the ability to move these funds cross-chain from their cross-chain account (executor)

`Non-Turing complete` - that there are no procedural loops or recursion, meaning that CVM programs are predictable, deterministic, and will stop
`Turing decidable language` - that there are no procedural loops or recursion, meaning that CVM programs are predictable, deterministic, and will stop

`Code propagated along with data` - you do not need to deploy programs, you just send programs; as subprograms move from chain to chain, they move assets along

Expand Down Expand Up @@ -179,7 +179,10 @@ type Query = RegisterValue | Id

type RegisterValue = RegisterValue[]

type RegisterValue = ResultRegister | IPRegister | TipRegister | SelfRegister | VersionRegister
type RegisterValue = ResultRegister | IPRegister | TipRegister | SelfRegister | VersionRegister | Carry

/// amount which was transferred into this Spawn (may be less then sent from original chain because if fee)
type Carry = {AssetId, AbsoluteAmount}[]

type IPRegister = Uint8

Expand Down Expand Up @@ -222,7 +225,6 @@ interface Abort {
abort_of_error: BindingValue
}


/// transfer from Executor account to
interface Transfer {
assets : Assets,
Expand All @@ -234,9 +236,11 @@ type Assets = [AssetId, Balance][]
type AssetId = GlobalId | LocalId
type GlobalId = Uint128
type LocalId = Uint8Array
type Balance = Ratio | Absolute | Unit
/// Absolute amount
type Absolute = Uint128

type Balance = { AbsoluteAmount, Ratio}

type AbsoluteAmount = Uint128

type Unit = Uint128 Ratio
/// parts of whole
type Ratio = { numerator : Uint64, denominator: Uint64}
Expand Down Expand Up @@ -385,7 +389,7 @@ The call instruction supports bindings values on the executing side of the progr

The binding value `Self` injects the account in the Executor into a call.

Besides accessing the `Self` register, `BindingValue` allows for lazy lookups of `AssetId` conversions. This is done by using `BindingValue::AssetId(GlobalId)`, or lazily converting `Ratio` to absolute `Balance` type.
Besides accessing the `Self` register, `BindingValue` allows for lazy lookups of `AssetId` conversions. This is done by using `BindingValue::AssetId(GlobalId)`, or lazily converting `Ratio` to `AbsoluteAmount` type.

Bindings support byte aligned encodings (all that are prominent in crypto).

Expand Down Expand Up @@ -437,7 +441,7 @@ Queries register values of a `CVM` instance across chains. It sets the current `

### Balances

The amount of assets can be specified using the `Balance` type. This allows foreign programs to specify sending a part of the total amount of funds using `Ratio`. Or, if the caller knows amount of the assets on the destination side using `Absolute`.
The amount of assets can be specified using the `Balance` type. This allows foreign programs to specify sending a part of the total amount of funds using `Ratio`. Or, if the caller knows amount of the assets on the destination side using `AbsoluteAmount`.

### Registers

Expand All @@ -457,11 +461,14 @@ The instruction pointer register contains the instruction pointer of the last ex

#### Tip Register

The Tip register contains the `Account` of the account triggering the initial execution. This can be the IBC relayer or any other entity. By definition, the tip is the account paying the fees for the Executor's execution.
The `Tip`` register contains the `Account` of the account which incentives relayers pay gas fees for program propagation from chain to chain and execution.
This can be the IBC relayer or incentive protocol.


#### Self Register

The self register contains the `Account` of the Executor. Most implementations will not need to use storage but have access to special keywords, such as `this` in Solidity.
The self register contains the `Account` of the `Executor`.
Most implementations will not need to use storage, but have access to special keywords, such as `this` in Solidity.

#### Version Register

Expand Down Expand Up @@ -513,18 +520,18 @@ A possible usage is to allow one program execution to act on the state of anothe

In the CVM program above, the parent program salt `0x01` is not a prefix of the sub-program salt `0x02`. The user is able to make its Executor origin using a fine grained mode. The following program is an example on how we can spread a salt:
```kdl
Spawn A 0x01 [ // Parent program spawned on A, with 0x01 as salt, the origin for the instructions is (A, AccountOnA, 0x01)
Call 0x1337, // Call instruction executed on A
Spawn B 0x0102 [] {}, // Sub-program spawned on B, with 0x0102 as salt, the origin for the instructions is (A, AccountOnA, 0x0102)
] {}
spawn A 0x01 { // Parent program spawned on A, with 0x01 as salt, the origin for the instructions is (A, AccountOnA, 0x01)
call 0x1337 // Call instruction executed on A
spawn B 0x0102 {}, // Sub-program spawned on B, with 0x0102 as salt, the origin for the instructions is (A, AccountOnA, 0x0102)
}
```

In next program, all spawned instances on all chains share state (including assets):
```kdl
Spawn A 0x01 [
Call 0x1337,
Spawn B 0x01 [] {}, // Sub-program spawned on B, with 0x01 as salt, the origin for the instructions is (A, AccountOnA, 0x01) allows to share
] {}
spawn A 0x01 {
call 0x1337
spawn B 0x01 {}, // Sub-program spawned on B, with 0x01 as salt, the origin for the instructions is (A, AccountOnA, 0x01) allows to share
}
```

### Ownership
Expand Down

0 comments on commit 6afbb34

Please sign in to comment.