Skip to content

Instantly share code, notes, and snippets.

@marcovc
Created July 4, 2024 06:56
Show Gist options
  • Save marcovc/582d5368dd1476ccddc3c5729450a9f0 to your computer and use it in GitHub Desktop.
Save marcovc/582d5368dd1476ccddc3c5729450a9f0 to your computer and use it in GitHub Desktop.
Encoding cow amm (in c++)
ExecFMAMMOrder::EncodingData ExecFMAMMOrder::compute_encoding_data(
std::chrono::system_clock::time_point const& valid_to
) const
{
using ABI = EthereumABIEncoding;
auto owner = order().fm_amm().id();
auto valid_to_seconds = std::chrono::duration_cast<std::chrono::seconds>(valid_to.time_since_epoch()).count();
auto encoding_parameters = order().fm_amm().encoding_parameters();
auto app_data_bytes = ABI::hex_string_as_bytes32(encoding_parameters.at("app_data").get<std::string>());
auto order_kind_bytes = ABI::hex_string_as_bytes32("0xf3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775"); // sell order
auto erc_token_balance_bytes = ABI::hex_string_as_bytes32("0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9"); // erc20
auto order = ABI::from_tuple({
ABI::from_address(sell_token().name()),
ABI::from_address(buy_token().name()),
ABI::from_address("0x0000000000000000000000000000000000000000"), // means the receiver=sender
ABI::from_uint(sell_amount_after_fees().convert_to<BigInt>()),
ABI::from_uint(min_buy_amount_after_fees().convert_to<BigInt>()),
ABI::from_uint(valid_to_seconds),
ABI::from_static_bytes(app_data_bytes),
ABI::from_uint(0), // order fee != executed order fee. Like limit orders, order fee is 0 meaning the solvers choose them
ABI::from_static_bytes(order_kind_bytes), // sell order
ABI::from_uint(0),
ABI::from_static_bytes(erc_token_balance_bytes), // erc20
ABI::from_static_bytes(erc_token_balance_bytes) // erc20
});
auto handler = encoding_parameters.at("handler").get<std::string>();
auto salt = encoding_parameters.at("salt").get<std::string>();
auto static_input = encoding_parameters.at("static_input").get<std::string>();
auto params = ABI::from_tuple({
ABI::from_address(handler),
ABI::from_static_bytes(ABI::hex_string_as_bytes32(salt)),
ABI::from_dynamic_bytes(ABI::hex_string_as_bytes(static_input))
});
// See https://newworkspace-nmq6115.slack.com/archives/C0690QX4R5X/p1719998847351919?thread_ts=1719040615.437539&cid=C0690QX4R5X.
auto payload_struct = ABI::from_tuple({
ABI::from_tuple(
{
ABI::from_dynamic_array({}), // proof
params,
ABI::from_dynamic_bytes({}) // offchainInput
}
)}
);
auto type_hash = ABI::hex_string_as_bytes32("0xd5a25ba2e97094ad7d83dc28a6572da797d6b3e7fc6663bd93efb789fc17e489");
auto separator = ABI::hex_string_as_bytes32("0xc078f884a2676e1345748b1feace7b0abee5d00ecadb6e574dcdd109a63e8943");
// As Martin explained https://newworkspace-nmq6115.slack.com/archives/C0690QX4R5X/p1718820504910619?thread_ts=1718818633.608049&cid=C0690QX4R5X
// the docs are wrong in the sense that the owner needs to be prepended in the signature:
auto signature_as_in_docs = ABI::to_calldata_bytes("safeSignature(bytes32,bytes32,bytes,bytes)", {
ABI::from_static_bytes(separator), // separator
ABI::from_static_bytes(type_hash), // type hash
ABI::from_dynamic_bytes(order.to_bytes()),
ABI::from_dynamic_bytes(payload_struct.to_bytes())
});
auto signature_bytes = hex_string_as_bytes(owner);
signature_bytes.insert(signature_bytes.end(), signature_as_in_docs.begin(), signature_as_in_docs.end());
auto signature = bytes_as_hex_string(signature_bytes);
// order hash. From https://github.com/cowprotocol/services/blob/main/crates/model/src/order.rs#L257-L279
std::vector<std::byte> eip712_message_hash;
{
std::vector<std::byte> order_hash;
std::vector<std::byte> hash_data(416, std::byte{0});
asserta(type_hash.size() == 32);
for (auto i = 0; i < type_hash.size(); ++i)
hash_data[i] = type_hash[i];
auto sell_token_bytes = hex_string_as_bytes(sell_token().name());
asserta(sell_token_bytes.size() == 20);
for (auto i = 0; i < sell_token_bytes.size(); ++i)
hash_data[i + 44] = sell_token_bytes[i];
auto buy_token_bytes = hex_string_as_bytes(buy_token().name());
asserta(buy_token_bytes.size() == 20);
for (auto i = 0; i < buy_token_bytes.size(); ++i)
hash_data[i + 76] = buy_token_bytes[i];
// Leaving receiver as 0x0
auto sell_amount_bytes = ABI::from_uint(sell_amount_after_fees().convert_to<BigInt>()).to_bytes();
asserta(sell_amount_bytes.size() == 32)
for (auto i = 0; i < sell_amount_bytes.size(); ++i)
hash_data[i + 128] = sell_amount_bytes[i];
auto min_buy_amount_bytes = ABI::from_uint(min_buy_amount_after_fees().convert_to<BigInt>()).to_bytes();
asserta(min_buy_amount_bytes.size() == 32)
for (auto i = 0; i < min_buy_amount_bytes.size(); ++i)
hash_data[i + 160] = min_buy_amount_bytes[i];
auto valid_to_bytes = uint_as_byte_vector(valid_to_seconds, 4);
asserta(valid_to_bytes.size() == 4);
for (auto i = 0; i < valid_to_bytes.size(); ++i)
hash_data[i + 220] = valid_to_bytes[i];
asserta(app_data_bytes.size() == 32);
for (auto i = 0; i < app_data_bytes.size(); ++i)
hash_data[i + 224] = app_data_bytes[i];
// Leaving fee amount as 0
asserta(order_kind_bytes.size() == 32);
for (auto i = 0; i < order_kind_bytes.size(); ++i)
hash_data[i + 288] = order_kind_bytes[i];
// Leaving partially fillable as 0
asserta(erc_token_balance_bytes.size() == 32);
for (auto i = 0; i < erc_token_balance_bytes.size(); ++i)
hash_data[i + 352] = erc_token_balance_bytes[i];
asserta(erc_token_balance_bytes.size() == 32);
for (auto i = 0; i < erc_token_balance_bytes.size(); ++i)
hash_data[i + 384] = erc_token_balance_bytes[i];
std::vector<std::byte> eip712_message(66, std::byte{0});
eip712_message[0] = std::byte{0x19};
eip712_message[1] = std::byte{0x01};
for (auto i = 0; i < 32; ++i)
eip712_message[i + 2] = separator[i];
order_hash = keccak256(hash_data);
for (auto i = 0; i < 32; ++i)
eip712_message[i + 34] = order_hash[i];
eip712_message_hash = keccak256(order_hash);
}
// pre interaction
auto pre_interaction = Interaction{
.target = "0x34323b933096534e43958f6c7bf44f2bb59424da",
.value = 0,
.call_data = ABI::to_calldata_hex_string("commit(address,bytes32)", {
ABI::from_address(owner),
ABI::from_static_bytes(eip712_message_hash)
}),
.exec_plan_coords = std::nullopt,
.buy_amount = std::nullopt,
.sell_amount = std::nullopt
};
auto empty_commitment = std::vector<std::byte>(32, std::byte{0});
auto post_interaction = Interaction{
.target = "0x34323b933096534e43958f6c7bf44f2bb59424da",
.value = 0,
.call_data = ABI::to_calldata_hex_string("commit(address,bytes32)", {
ABI::from_address(owner),
ABI::from_static_bytes(empty_commitment)
}),
.exec_plan_coords = std::nullopt,
.buy_amount = std::nullopt,
.sell_amount = std::nullopt
};
return {
.signature = signature,
.pre_interaction = pre_interaction,
.post_interaction = post_interaction
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment