Skip to content

Commit b0988db

Browse files
feat(zkgm): introduce solve kind for token order (#4988)
2 parents b5a6fcb + cf7cb35 commit b0988db

File tree

15 files changed

+189
-1111
lines changed

15 files changed

+189
-1111
lines changed

cosmwasm/cw-escrow-vault/src/contract.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,16 @@ pub fn execute(
130130
channel_id: packet.destination_channel_id,
131131
})?;
132132

133+
let quote_token = String::from_utf8(Vec::from(order.quote_token))
134+
.map_err(|_| Error::InvalidQuoteToken)?;
135+
136+
if quote_token != fungible_lane.escrowed_denom {
137+
return Err(Error::InvalidFill {
138+
quote_token,
139+
escrowed_denom: fungible_lane.escrowed_denom,
140+
});
141+
}
142+
133143
let mut messages = Vec::<CosmosMsg>::with_capacity(2);
134144
let mut push_transfer = |to, amount: Uint128| -> StdResult<()> {
135145
if !amount.is_zero() {

cosmwasm/cw-escrow-vault/src/error.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,13 @@ pub enum Error {
2727

2828
#[error("the lane has not been configured to be fungible")]
2929
LaneIsNotFungible { channel_id: ChannelId },
30+
31+
#[error("the quote token must be a valid utf8 denom")]
32+
InvalidQuoteToken,
33+
34+
#[error("the order quote token must match the previously escrowed denom: quote_token={quote_token} != escrowed_denom={escrowed_denom}")]
35+
InvalidFill {
36+
quote_token: String,
37+
escrowed_denom: String,
38+
},
3039
}

cosmwasm/ibc-union/app/ucs03-zkgm/src/com.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub const FILL_TYPE_MARKETMAKER: U256 = U256::from_be_slice(&[0xD1, 0xCE, 0xC4,
2828
pub const TOKEN_ORDER_KIND_INITIALIZE: u8 = 0x00;
2929
pub const TOKEN_ORDER_KIND_ESCROW: u8 = 0x01;
3030
pub const TOKEN_ORDER_KIND_UNESCROW: u8 = 0x02;
31+
pub const TOKEN_ORDER_KIND_SOLVE: u8 = 0x03;
3132

3233
pub const FORWARD_SALT_MAGIC: U256 = U256::from_be_slice(&[
3334
0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -100,6 +101,12 @@ alloy_sol_types::sol! {
100101
bytes initializer;
101102
}
102103

104+
#[derive(Debug, PartialEq)]
105+
struct SolverMetadata {
106+
bytes solverAddress;
107+
bytes metadata;
108+
}
109+
103110
#[derive(Debug, PartialEq)]
104111
struct Stake {
105112
uint256 token_id;

cosmwasm/ibc-union/app/ucs03-zkgm/src/contract.rs

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,17 @@ use unionlabs::{
2626

2727
use crate::{
2828
com::{
29-
Ack, Batch, BatchAck, Call, Forward, Instruction, Stake, TokenMetadata, TokenOrderAck,
30-
TokenOrderV1, TokenOrderV2, Unstake, UnstakeAck, WithdrawRewards, WithdrawRewardsAck,
31-
WithdrawStake, WithdrawStakeAck, ZkgmPacket, ACK_ERR_ONLY_MAKER, FILL_TYPE_MARKETMAKER,
32-
FILL_TYPE_PROTOCOL, FORWARD_SALT_MAGIC, INSTR_VERSION_0, INSTR_VERSION_1, INSTR_VERSION_2,
33-
OP_BATCH, OP_CALL, OP_FORWARD, OP_STAKE, OP_TOKEN_ORDER, OP_UNSTAKE, OP_WITHDRAW_REWARDS,
34-
OP_WITHDRAW_STAKE, TAG_ACK_FAILURE, TAG_ACK_SUCCESS, TOKEN_ORDER_KIND_ESCROW,
35-
TOKEN_ORDER_KIND_INITIALIZE, TOKEN_ORDER_KIND_UNESCROW,
29+
Ack, Batch, BatchAck, Call, Forward, Instruction, SolverMetadata, Stake, TokenMetadata,
30+
TokenOrderAck, TokenOrderV1, TokenOrderV2, Unstake, UnstakeAck, WithdrawRewards,
31+
WithdrawRewardsAck, WithdrawStake, WithdrawStakeAck, ZkgmPacket, ACK_ERR_ONLY_MAKER,
32+
FILL_TYPE_MARKETMAKER, FILL_TYPE_PROTOCOL, FORWARD_SALT_MAGIC, INSTR_VERSION_0,
33+
INSTR_VERSION_1, INSTR_VERSION_2, OP_BATCH, OP_CALL, OP_FORWARD, OP_STAKE, OP_TOKEN_ORDER,
34+
OP_UNSTAKE, OP_WITHDRAW_REWARDS, OP_WITHDRAW_STAKE, TAG_ACK_FAILURE, TAG_ACK_SUCCESS,
35+
TOKEN_ORDER_KIND_ESCROW, TOKEN_ORDER_KIND_INITIALIZE, TOKEN_ORDER_KIND_SOLVE,
36+
TOKEN_ORDER_KIND_UNESCROW,
3637
},
3738
msg::{
38-
Config, ExecuteMsg, InitMsg, PredictWrappedTokenResponse, QueryMsg, SolverMsg, SolverQuery,
39+
Config, ExecuteMsg, InitMsg, PredictWrappedTokenResponse, QueryMsg, SolverMsg,
3940
V1ToV2Migration, ZkgmMsg,
4041
},
4142
state::{
@@ -1776,11 +1777,12 @@ fn solver_market_maker_fill_v2(
17761777
order: TokenOrderV2,
17771778
intent: bool,
17781779
) -> Result<Response, ContractError> {
1779-
let quote_token_str = String::from_utf8(Vec::from(order.quote_token.clone()))
1780-
.map_err(|_| ContractError::InvalidQuoteToken)?;
1780+
let metadata = SolverMetadata::abi_decode_params_validate(&order.metadata)?;
1781+
let solver = String::from_utf8(Vec::from(metadata.solverAddress))
1782+
.map_err(|_| ContractError::InvalidSolverAddress)?;
17811783
Ok(Response::new().add_submessage(SubMsg::reply_always(
17821784
wasm_execute(
1783-
quote_token_str.clone(),
1785+
solver,
17841786
&SolverMsg::DoSolve {
17851787
packet,
17861788
order: order.into(),
@@ -1810,35 +1812,14 @@ fn market_maker_fill_v2(
18101812
order: TokenOrderV2,
18111813
intent: bool,
18121814
) -> Result<Response, ContractError> {
1813-
let quote_token_str = String::from_utf8(order.quote_token.clone().into())
1814-
.map_err(|_| ContractError::InvalidQuoteToken)?;
1815-
let is_solver = deps
1816-
.querier
1817-
.query_wasm_smart::<()>(quote_token_str.clone(), &SolverQuery::IsSolver)
1818-
.is_ok();
1819-
let allow_market_maker = deps
1820-
.querier
1821-
.query_wasm_smart::<bool>(quote_token_str.clone(), &SolverQuery::AllowMarketMakers)
1822-
.unwrap_or(false);
1823-
let (relayer_fill_for_solver, arbitrary_relayer_payload) =
1824-
match <(bool, alloy_primitives::Bytes)>::abi_decode_params(&relayer_msg) {
1825-
Ok((x, y)) => (x, y),
1826-
Err(_) => (false, relayer_msg.into_vec().into()),
1827-
};
1828-
let arbitrary_relayer_payload = arbitrary_relayer_payload.0.to_vec().into();
1829-
if is_solver && (!allow_market_maker || !relayer_fill_for_solver) {
1830-
solver_market_maker_fill_v2(
1831-
caller,
1832-
relayer,
1833-
arbitrary_relayer_payload,
1834-
path,
1835-
packet,
1836-
order,
1837-
intent,
1838-
)
1839-
} else {
1840-
MARKET_MAKER.save(deps.storage, &arbitrary_relayer_payload)?;
1841-
relayer_market_maker_fill_v2(deps, env, funds, caller, minter, order)
1815+
match order.kind {
1816+
TOKEN_ORDER_KIND_SOLVE => {
1817+
solver_market_maker_fill_v2(caller, relayer, relayer_msg, path, packet, order, intent)
1818+
}
1819+
_ => {
1820+
MARKET_MAKER.save(deps.storage, &relayer_msg)?;
1821+
relayer_market_maker_fill_v2(deps, env, funds, caller, minter, order)
1822+
}
18421823
}
18431824
}
18441825

@@ -2082,7 +2063,7 @@ fn execute_fungible_asset_order_v2(
20822063
.map_err(|_| ContractError::UnableToValidateReceiver)?;
20832064

20842065
// For intent packets, only market maker can fill
2085-
if intent {
2066+
if intent || order.kind == TOKEN_ORDER_KIND_SOLVE {
20862067
let minter = TOKEN_MINTER.load(deps.storage)?;
20872068
return market_maker_fill_v2(
20882069
deps,

cosmwasm/ibc-union/app/ucs03-zkgm/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub enum ContractError {
4747
AmountOverflow,
4848
#[error("the quote token must be a valid utf8 denom")]
4949
InvalidQuoteToken,
50+
#[error("the solver must be a bech32 encoded contract address")]
51+
InvalidSolverAddress,
5052
#[error("the base token must be a valid utf8 denom")]
5153
InvalidBaseToken,
5254
#[error("invalid channel balance, counterparty has been taken over?")]

cosmwasm/ibc-union/app/ucs03-zkgm/src/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1649,6 +1649,7 @@ fn test_recv_packet_native_base_dont_cover_quote_only_maker() {
16491649
assert!(st.app.contract_data(&quote_token_addr).is_err());
16501650
let (_, msg, _) = IncomingOrderBuilder::new(quote_token_addr.clone().into())
16511651
.with_base_token(base_token)
1652+
.with_kind(TOKEN_ORDER_KIND_ESCROW)
16521653
.with_destination_channel_id(destination_channel_id)
16531654
.with_path(path)
16541655
// Base not covering the quote

cosmwasm/ucs03-zkgm-token-minter-api/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ pub fn new_wrapped_token_event(
3535
EVENT_WRAPPED_TOKEN_ATTR_BASE_TOKEN,
3636
Bytes::<HexPrefixed>::from(base_token).to_string(),
3737
)
38-
.add_attribute(EVENT_WRAPPED_TOKEN_ATTR_QUOTE_TOKEN, quote_token_denom)
38+
.add_attribute(
39+
EVENT_WRAPPED_TOKEN_ATTR_QUOTE_TOKEN,
40+
Bytes::<HexPrefixed>::from(quote_token_denom.as_bytes()).to_string(),
41+
)
3942
.add_attribute(
4043
EVENT_WRAPPED_TOKEN_ATTR_METADATA,
4144
Bytes::<HexPrefixed>::from(metadata).to_string(),

deployments/deployments.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@
5555
"core": {
5656
"address": "bbn1hnuj8f6d3wy3fcprt55vddv7v2650t6uudnvd2hukqrteeam8wjqnlye9f",
5757
"height": 714672,
58-
"commit": "e6d7b21598b95b2244bf82b242d97606585e6c25",
59-
"code_id": 566
58+
"commit": "6609d7359e46d6bf3de739056b27666c7de322b0",
59+
"code_id": 811
6060
},
6161
"lightclient": {
6262
"cometbls": {
@@ -94,8 +94,8 @@
9494
"ucs03": {
9595
"address": "bbn1336jj8ertl8h7rdvnz4dh5rqahd09cy0x43guhsxx6xyrztx292q77945h",
9696
"height": 714737,
97-
"commit": "dirty",
98-
"code_id": 808,
97+
"commit": "8394c0d5f90649ed3d2bf2b5745592312888d359",
98+
"code_id": 815,
9999
"minter": {
100100
"type": "cw20",
101101
"address": "bbn1sakazthycqgzer50nqgr5ta4vy3gwz8wxla3s8rd8pql4ctmz5qssg39sf",
@@ -461,7 +461,7 @@
461461
"ucs03": {
462462
"address": "0x5fbe74a283f7954f10aa04c2edf55578811aeb03",
463463
"height": 8088109,
464-
"commit": "623ccb60b217c45ac5f2d1cdd6e5f8e5681bb3a8"
464+
"commit": "1e67026e83bc299d8766625a921f86d422ee427d"
465465
}
466466
}
467467
},
@@ -501,7 +501,7 @@
501501
"ucs03": {
502502
"address": "0x5fbe74a283f7954f10aa04c2edf55578811aeb03",
503503
"height": 3646606,
504-
"commit": "623ccb60b217c45ac5f2d1cdd6e5f8e5681bb3a8"
504+
"commit": "1e67026e83bc299d8766625a921f86d422ee427d"
505505
}
506506
}
507507
},
@@ -788,8 +788,8 @@
788788
"ucs03": {
789789
"address": "union1336jj8ertl8h7rdvnz4dh5rqahd09cy0x43guhsxx6xyrztx292qpe64fh",
790790
"height": 354920,
791-
"commit": "48aff3edbff5edf36542570a15f42e9777340f16",
792-
"code_id": 147,
791+
"commit": "9f3d326626cdc2f8477e2135b2a2ce2026d823f8",
792+
"code_id": 154,
793793
"minter": {
794794
"type": "cw20",
795795
"address": "union1t5awl707x54k6yyx7qfkuqp890dss2pqgwxh07cu44x5lrlvt4rs8hqmk0",

evm/contracts/U.sol

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol";
55
import "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
66
import
77
"@openzeppelin-upgradeable/contracts/access/manager/AccessManagedUpgradeable.sol";
8-
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
98

109
import "solady/utils/LibBytes.sol";
1110

@@ -19,8 +18,7 @@ contract U is
1918
AccessManagedUpgradeable,
2019
ERC20Upgradeable,
2120
Versioned,
22-
ISolver,
23-
ERC165
21+
ISolver
2422
{
2523
using LibBytes for *;
2624

@@ -29,6 +27,7 @@ contract U is
2927
error U_CounterpartyIsNotFungible();
3028
error U_BaseAmountMustCoverQuoteAmount();
3129
error U_InvalidCounterpartyBeneficiary();
30+
error U_Fool();
3231

3332
bytes32 internal constant U_STORAGE_SLOT = keccak256(
3433
abi.encode(uint256(keccak256("union.storage.zkgm.u")) - 1)
@@ -173,6 +172,10 @@ contract U is
173172
revert U_CounterpartyIsNotFungible();
174173
}
175174

175+
if (!order.quoteToken.eq(abi.encodePacked(address(this)))) {
176+
revert U_Fool();
177+
}
178+
176179
if (order.quoteAmount > order.baseAmount) {
177180
revert U_BaseAmountMustCoverQuoteAmount();
178181
}
@@ -192,13 +195,6 @@ contract U is
192195
return counterparty.beneficiary;
193196
}
194197

195-
function supportsInterface(
196-
bytes4 interfaceId
197-
) public view override returns (bool) {
198-
return interfaceId == type(ISolver).interfaceId
199-
|| super.supportsInterface(interfaceId);
200-
}
201-
202198
modifier onlyZkgm() {
203199
if (msg.sender != _getUStorage().zkgm) {
204200
revert U_OnlyZkgm();

evm/contracts/apps/ucs/03-zkgm/Lib.sol

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ library ZkgmLib {
2929
uint8 public constant TOKEN_ORDER_KIND_INITIALIZE = 0x00;
3030
uint8 public constant TOKEN_ORDER_KIND_ESCROW = 0x01;
3131
uint8 public constant TOKEN_ORDER_KIND_UNESCROW = 0x02;
32+
uint8 public constant TOKEN_ORDER_KIND_SOLVE = 0x03;
3233

3334
// Public instructions
3435
uint8 public constant OP_FORWARD = 0x00;
@@ -125,6 +126,16 @@ library ZkgmLib {
125126
return meta;
126127
}
127128

129+
function decodeSolverMetadata(
130+
bytes calldata stream
131+
) internal pure returns (SolverMetadata calldata) {
132+
SolverMetadata calldata meta;
133+
assembly {
134+
meta := stream.offset
135+
}
136+
return meta;
137+
}
138+
128139
function decodeUnstakeAck(
129140
bytes calldata stream
130141
) internal pure returns (UnstakeAck calldata) {
@@ -412,6 +423,12 @@ library ZkgmLib {
412423
return abi.encode(meta.implementation, meta.initializer);
413424
}
414425

426+
function encodeSolverMetadata(
427+
SolverMetadata memory meta
428+
) internal pure returns (bytes memory) {
429+
return abi.encode(meta.solverAddress, meta.metadata);
430+
}
431+
415432
function decodeTokenOrderV1(
416433
bytes calldata stream
417434
) internal pure returns (TokenOrderV1 calldata) {
@@ -627,10 +644,4 @@ library ZkgmLib {
627644
) internal pure returns (bool) {
628645
return instruction.opcode == opcode && instruction.version == version;
629646
}
630-
631-
function isSolver(
632-
address token
633-
) internal view returns (bool) {
634-
return ERC165Checker.supportsInterface(token, type(ISolver).interfaceId);
635-
}
636647
}

0 commit comments

Comments
 (0)