Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup contract codebase and withdraw signed signature verification #8

Merged
merged 5 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,952 changes: 1,771 additions & 181 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ If you hit any issues there and want to debug, you can try to run the
following in each contract dir:
`RUSTFLAGS="-C link-arg=-s" cargo build --release --target=wasm32-unknown-unknown --locked`

## Unit test

To run overall tests:

```
cargo test
```

To run specific test:

```
cargo test <test-name>
```

## Quality Control

One of the basic metrics of assurance over code quality is how much is covered by
Expand Down
16 changes: 5 additions & 11 deletions contracts/fiberrouter-base/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
use cosmwasm_std::entry_point;
use cosmwasm_std::{
coins, to_binary, Addr, Api, BankMsg, Binary, Coin, CosmosMsg, Deps, DepsMut, Env, MessageInfo,
Order, Response, StdError, StdResult, Storage, Uint128,
to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, Uint128,
};
use cw_storage_plus::Bound;

use fiberrouter::{
FiberRouterExecuteMsg, FiberRouterQueryMsg, MigrateMsg, SetPoolEvent, TransferOwnershipEvent,
};
use multiswap::{MultiswapContract, MultiswapExecuteMsg};

use crate::error::{self, ContractError};
use crate::error::ContractError;
use crate::msg::InstantiateMsg;
use crate::state::{OWNER, POOL};
use cw_utils::Event;

// version info for migration info
const CONTRACT_NAME: &str = "crates.io:fiberrouter-base";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut,
Expand Down Expand Up @@ -81,7 +75,7 @@ pub fn execute_ownership_transfer(
env: ExecuteEnv,
new_owner: String,
) -> Result<Response, ContractError> {
let ExecuteEnv { deps, env, info } = env;
let ExecuteEnv { deps, env: _, info } = env;
let new_owner_addr = deps.api.addr_validate(&new_owner)?;

if info.sender != OWNER.load(deps.storage)? {
Expand All @@ -100,7 +94,7 @@ pub fn execute_ownership_transfer(
}

pub fn execute_set_pool(env: ExecuteEnv, new_pool: String) -> Result<Response, ContractError> {
let ExecuteEnv { deps, env, info } = env;
let ExecuteEnv { deps, env: _, info } = env;
let new_pool_addr = deps.api.addr_validate(&new_pool)?;

if info.sender != OWNER.load(deps.storage)? {
Expand Down Expand Up @@ -160,7 +154,7 @@ pub fn execute_swap(
target_token: String,
target_address: String,
) -> Result<Response, ContractError> {
let ExecuteEnv { deps, env, info } = env;
let ExecuteEnv { deps, env: _, info } = env;
let pool = POOL.load(deps.storage)?;
let contract_addr = deps.api.addr_validate(pool.as_str())?;
// MultiswapContract is a function helper that provides several queries and message builder.
Expand Down
4 changes: 2 additions & 2 deletions contracts/fiberrouter-base/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use cosmwasm_std::{Addr, Uint128};
use cw_storage_plus::{Item, Map};
use cosmwasm_std::Addr;
use cw_storage_plus::Item;

/// Store the owner of the contract to set pool
pub const OWNER: Item<Addr> = Item::new("owner");
Expand Down
3 changes: 3 additions & 0 deletions contracts/multiswap-base/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ cosmwasm-std = { version = "1.0.0" }
schemars = "0.8.1"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { version = "1.0.20" }
web3 = { version = "0.17.0" }
hex = { version = "0.4"}
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }

[dev-dependencies]
cosmwasm-schema = { version = "1.0.0" }
157 changes: 108 additions & 49 deletions contracts/multiswap-base/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
use cosmwasm_std::entry_point;
use cosmwasm_std::{
coins, to_binary, Addr, Api, BankMsg, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo,
Order, Response, StdError, StdResult, Storage, Uint128,
coins, to_binary, Api, BankMsg, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Order,
Response, StdError, StdResult, Storage, Uint128,
};
use cw_storage_plus::Bound;

use multiswap::{
AddFoundryAssetEvent, AddLiquidityEvent, AddSignerEvent, BridgeSwapEvent,
BridgeWithdrawSignedEvent, Liquidity, MigrateMsg, MultiswapExecuteMsg, MultiswapQueryMsg,
RemoveFoundryAssetEvent, RemoveLiquidityEvent, RemoveSignerEvent, TransferOwnershipEvent,
WithdrawSignMessage,
};

use crate::error::{self, ContractError};
use web3::signing::{keccak256, recover};

use crate::error::ContractError;
use crate::msg::InstantiateMsg;
use crate::state::{FOUNDRY_ASSETS, LIQUIDITIES, OWNER, SIGNERS};
use cw_utils::Event;
// use crate::state::{APPROVES, BALANCES, MINTER, TOKENS};

// version info for migration info
const CONTRACT_NAME: &str = "crates.io:multiswap-base";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
Expand Down Expand Up @@ -302,16 +299,20 @@ pub fn execute_withdraw_signed(

let payee_addr = env.deps.api.addr_validate(&payee)?;

// TODO: gets signer from params
// signer, messageBytes, err := get_signer(ctx.ChainID(), payee, amount, salt, signature)
// if err != nil {
// return types.ErrUnexpectedError(k.codespace, err)
// }
// gets signer from signature recovery
let signer = get_signer(
env.env.block.chain_id,
payee.to_string(),
token.to_string(),
amount,
salt.to_string(),
signature.to_string(),
);

// TODO: ensure that the signer is registered on-chain
// if is_signer(env.storage, signer.String()) {
// return types.ErrInvalidSigner(k.codespace)
// }
// ensure that the signer is registered on-chain
if !is_signer(env.deps.storage, signer.to_string()) {
return Err(ContractError::Unauthorized {});
}

// TODO: avoid using same signature and salt again
// if k.IsUsedMessage(ctx, messageBytes) {
Expand All @@ -325,7 +326,7 @@ pub fn execute_withdraw_signed(

let ExecuteEnv {
mut deps,
env,
env: _,
info,
} = env;

Expand All @@ -335,7 +336,8 @@ pub fn execute_withdraw_signed(
payee: payee.as_str(),
token: token.as_str(),
amount,
salt: &salt,
signer: signer.as_str(),
salt: salt.as_str(),
signature: &signature,
};
event.add_attributes(&mut rsp);
Expand Down Expand Up @@ -382,35 +384,55 @@ pub fn execute_swap(
Ok(rsp)
}

// TODO: get_signer calculate signer from withdraw signed message parameters
// pub fn get_signer(chainId string, payee string, amount sdk.Coin,
// salt string, signature []byte) (common.Address, []byte, error) {
// signer := common.Address{}

// // get sign message to be used for signature verification
// message := &types.WithdrawSignMessage{
// ChainId: chainId,
// Payee: payee,
// Amount: amount,
// Salt: salt,
// }
// messageBytes, err := json.Marshal(message)
// if err != nil {
// return signer, messageBytes, err
// }

// // get signer from sign message and signature
// if len(signature) > crypto.RecoveryIDOffset {
// signature[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1
// recovered, err := crypto.SigToPub(accounts.TextHash(messageBytes), signature)
// if err != nil {
// return signer, messageBytes, err
// }
// signer = crypto.PubkeyToAddress(*recovered)
// }

// return signer, messageBytes, nil
// }
pub fn eth_message(message: String) -> [u8; 32] {
keccak256(
format!(
"{}{}{}",
"\x19Ethereum Signed Message:\n",
message.len(),
message
)
.as_bytes(),
)
}

// get_signer calculate signer from withdraw signed message parameters
pub fn get_signer(
chain_id: String,
payee: String,
token: String,
amount: Uint128,
salt: String,
signature: String,
) -> String {
// get sign message to be used for signature verification
let message_obj = WithdrawSignMessage {
chain_id: chain_id,
payee: payee,
token: token,
amount: amount,
salt: salt,
};

// Serialize it to a JSON string.
let message_encoding = serde_json::to_string(&message_obj);
let mut message = "".to_string();
if let Ok(message_str) = message_encoding {
message = message_str.to_string()
}

let message = eth_message(message);
let signature = hex::decode(signature).unwrap();

let recovery_id = signature[64] as i32 - 27;
let pubkey = recover(&message, &signature[..64], recovery_id);
let pubkey = pubkey.unwrap();
let pubkey = format!("{:02X?}", pubkey);

// https://github.com/tomusdrw/rust-web3/issues/564
// https://crates.io/crates/ethsign
return pubkey;
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: MultiswapQueryMsg) -> StdResult<Binary> {
Expand Down Expand Up @@ -549,3 +571,40 @@ pub fn is_foundry_asset(storage: &dyn Storage, foundry_asset: String) -> bool {
pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
Ok(Response::default())
}

#[cfg(test)]
mod test {
use super::*;

use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coins, from_binary, StdError};

#[test]
fn test_get_signer() {
/////////// signature generation script ///////////
// require("dotenv").config();
// const { Wallet, utils, providers } = require("ethers");
// const messageText =
// '{"chain_id":"chain_id","payee":"payee","token":"token","amount":"10000","salt":"salt"}';
// let provider = new providers.JsonRpcProvider();
// let privKey = process.env.PRIVATE_KEY;
// const wallet = new Wallet(privKey, provider);
// const signature = await wallet.signMessage(messageText);
// console.log("signature", signature);
// console.log("address", wallet.address);

let signer = get_signer(
"chain_id".to_string(),
"payee".to_string(),
"token".to_string(),
Uint128::new(10000),
"salt".to_string(),
"a112b6508d535f091b5de8877b34213aebca322c8a4edfbdb5002416343f30b06c387379293502b43e912350693e141ce814ef507abb007a4604f3ea73f94c691b".to_string(),
);

assert_eq!(
"0x035567da27e42258c35b313095acdea4320a7465".to_string(),
signer
);
}
}
2 changes: 1 addition & 1 deletion contracts/multiswap-base/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use cosmwasm_std::{Addr, Uint128};
use cosmwasm_std::Addr;
use cw_storage_plus::{Item, Map};
use multiswap::Liquidity;

Expand Down
Loading